/**
 * MVGAL NTSYNC Kernel Module - Wine NT Synchronization Primitives
 *
 * Provides NT-style synchronization objects for Wine/Proton integration:
 * - Mutexes (fast mutexes)
 * - Events (notification events, auto-reset events)
 * - Semaphores (counting semaphores)
 * - Timers
 * - Critical sections
 *
 * Integrates with MVGAL's cross-GPU fence system for zero-CPU-stall synchronization.
 *
 * SPDX-License-Identifier: GPL-2.0
 */

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
#include <linux/slab.h>
#include <linux/dma-fence.h>
#include <linux/dma-buf.h>
#include <linux/anon_inodes.h>
#include <linux/poll.h>
#include <linux/atomic.h>

#define MVGAL_NTSYNC_VERSION "0.2.7"

/* ------------------------------------------------------------------ */
/*  NTSYNC Object Types                                                */
/* ------------------------------------------------------------------ */

#define MVGAL_NTSYNC_TYPE_EVENT       1
#define MVGAL_NTSYNC_TYPE_SEMAPHORE   2
#define MVGAL_NTSYNC_TYPE_MUTEX       3
#define MVGAL_NTSYNC_TYPE_TIMER       4
#define MVGAL_NTSYNC_TYPE_FENCE       5

/* ------------------------------------------------------------------ */
/*  NTSYNC Object Flags                                                 */
/* ------------------------------------------------------------------ */

#define MVGAL_NTSYNC_FLAG_AUTO_RESET   (1 << 0)  /* Auto-reset event */
#define MVGAL_NTSYNC_FLAG_SIGNALED     (1 << 1)  /* Initially signaled */
#define MVGAL_NTSYNC_FLAG_SHARED       (1 << 2)  /* Shared across processes */

/* ------------------------------------------------------------------ */
/*  NTSYNC IOCTL Definitions                                            */
/* ------------------------------------------------------------------ */

#define MVGAL_NTSYNC_IOC_MAGIC 'N'

#define MVGAL_NTSYNC_IOC_CREATE_EVENT     _IOWR(MVGAL_NTSYNC_IOC_MAGIC, 1, struct mvgal_ntsync_create_args)
#define MVGAL_NTSYNC_IOC_OPEN_EVENT       _IOW(MVGAL_NTSYNC_IOC_MAGIC, 2, struct mvgal_ntsync_open_args)
#define MVGAL_NTSYNC_IOC_WAIT_EVENT       _IOWR(MVGAL_NTSYNC_IOC_MAGIC, 3, struct mvgal_ntsync_wait_args)
#define MVGAL_NTSYNC_IOC_SIGNAL_EVENT     _IOW(MVGAL_NTSYNC_IOC_MAGIC, 4, struct mvgal_ntsync_signal_args)
#define MVGAL_NTSYNC_IOC_CREATE_SEMAPHORE _IOWR(MVGAL_NTSYNC_IOC_MAGIC, 5, struct mvgal_ntsync_create_args)
#define MVGAL_NTSYNC_IOC_WAIT_SEMAPHORE    _IOWR(MVGAL_NTSYNC_IOC_MAGIC, 6, struct mvgal_ntsync_wait_args)
#define MVGAL_NTSYNC_IOC_RELEASE_SEMAPHORE  _IOW(MVGAL_NTSYNC_IOC_MAGIC, 7, struct mvgal_ntsync_signal_args)
#define MVGAL_NTSYNC_IOC_CREATE_MUTEX     _IOWR(MVGAL_NTSYNC_IOC_MAGIC, 8, struct mvgal_ntsync_create_args)
#define MVGAL_NTSYNC_IOC_ACQUIRE_MUTEX      _IOWR(MVGAL_NTSYNC_IOC_MAGIC, 9, struct mvgal_ntsync_wait_args)
#define MVGAL_NTSYNC_IOC_RELEASE_MUTEX      _IOW(MVGAL_NTSYNC_IOC_MAGIC, 10, struct mvgal_ntsync_signal_args)
#define MVGAL_NTSYNC_IOC_CREATE_TIMER     _IOWR(MVGAL_NTSYNC_IOC_MAGIC, 11, struct mvgal_ntsync_create_args)
#define MVGAL_NTSYNC_IOC_SET_TIMER        _IOW(MVGAL_NTSYNC_IOC_MAGIC, 12, struct mvgal_ntsync_timer_args)
#define MVGAL_NTSYNC_IOC_CREATE_FENCE     _IOWR(MVGAL_NTSYNC_IOC_MAGIC, 13, struct mvgal_ntsync_fence_args)
#define MVGAL_NTSYNC_IOC_WAIT_FENCE       _IOWR(MVGAL_NTSYNC_IOC_MAGIC, 14, struct mvgal_ntsync_wait_args)
#define MVGAL_NTSYNC_IOC_SIGNAL_FENCE     _IOW(MVGAL_NTSYNC_IOC_MAGIC, 15, struct mvgal_ntsync_signal_args)

/* ------------------------------------------------------------------ */
/*  NTSYNC Structures                                                   */
/* ------------------------------------------------------------------ */

struct mvgal_ntsync_create_args {
    __u32 type;        /* MVGAL_NTSYNC_TYPE_* */
    __u32 flags;       /* MVGAL_NTSYNC_FLAG_* */
    __u32 initial;     /* Initial count (semaphore) or 0 */
    __u32 fd;          /* Output: file descriptor */
};

struct mvgal_ntsync_open_args {
    __u32 fd;          /* Input: file descriptor to duplicate */
};

struct mvgal_ntsync_wait_args {
    __u32 fd;          /* Input: file descriptor */
    __u32 status;      /* Output: 0=signaled, 1=timed out, 2=abandoned */
    __u64 timeout_ns;  /* Timeout in nanoseconds (0 = INFINITE) */
};

struct mvgal_ntsync_signal_args {
    __u32 fd;          /* Input: file descriptor */
    __u32 count;       /* For semaphores: release count */
    __u32 alertable;   /* Whether to return for APCs */
};

struct mvgal_ntsync_timer_args {
    __u32 fd;          /* Input: timer file descriptor */
    __u64 due_time_ns; /* Due time in nanoseconds */
    __u64 period_ns;   /* Period (0 = one-shot) */
};

struct mvgal_ntsync_fence_args {
    __u32 fd;          /* Output: fence FD */
    __u32 gpu_index;   /* GPU index for cross-GPU fence */
    __u32 flags;       /* Fence flags */
    __u64 timeout_ns;  /* Timeout for operations */
};

/* ------------------------------------------------------------------ */
/*  NTSYNC Object Structure                                             */
/* ------------------------------------------------------------------ */

struct mvgal_ntsync_object {
    struct list_head node;
    atomic_t refcount;

    __u32 type;
    __u32 flags;
    __u32 fd;          /* Internal FD */

    /* Event fields */
    atomic_t signaled;
    atomic_t waiters;
    wait_queue_head_t waitq;

    /* Semaphore fields */
    atomic_t count;
    atomic_t max_count;

    /* Fence fields */
    struct dma_fence *dma_fence;

    /* Timer fields */
    struct timer_list timer;
    atomic_t timer_expired;

    /* Mutex owner tracking */
    __u32 owner_tid;
};

/* ------------------------------------------------------------------ */
/*  Global State                                                        */
/* ------------------------------------------------------------------ */

static DEFINE_SPINLOCK(ntsync_lock);
static LIST_HEAD(ntsync_objects);
static __u32 g_next_fd = 0x10000000;

static const struct file_operations ntsync_fops;

/* ------------------------------------------------------------------ */
/*  Object Management                                                   */
/* ------------------------------------------------------------------ */

static struct mvgal_ntsync_object *find_object_by_fd(__u32 fd)
{
    struct mvgal_ntsync_object *obj;

    spin_lock(&ntsync_lock);
    list_for_each_entry(obj, &ntsync_objects, node) {
        if (obj->fd == fd) {
            atomic_inc(&obj->refcount);
            spin_unlock(&ntsync_lock);
            return obj;
        }
    }
    spin_unlock(&ntsync_lock);
    return NULL;
}

static void release_object(struct mvgal_ntsync_object *obj)
{
    if (!obj)
        return;

    if (atomic_dec_and_test(&obj->refcount)) {
        spin_lock(&ntsync_lock);
        list_del(&obj->node);
        spin_unlock(&ntsync_lock);

        if (obj->dma_fence)
            dma_fence_put(obj->dma_fence);

        kfree(obj);
    }
}

static struct mvgal_ntsync_object *create_object(__u32 type, __u32 flags)
{
    struct mvgal_ntsync_object *obj;

    obj = kzalloc(sizeof(*obj), GFP_KERNEL);
    if (!obj)
        return NULL;

    atomic_set(&obj->refcount, 1);
    obj->type = type;
    obj->flags = flags;
    obj->fd = g_next_fd++;
    init_waitqueue_head(&obj->waitq);

    switch (type) {
    case MVGAL_NTSYNC_TYPE_EVENT:
        atomic_set(&obj->signaled, flags & MVGAL_NTSYNC_FLAG_SIGNALED ? 1 : 0);
        atomic_set(&obj->waiters, 0);
        break;

    case MVGAL_NTSYNC_TYPE_SEMAPHORE:
        atomic_set(&obj->count, 0);
        atomic_set(&obj->max_count, 0);
        break;

    case MVGAL_NTSYNC_TYPE_MUTEX:
        atomic_set(&obj->signaled, 1); /* Unlocked */
        obj->owner_tid = 0;
        break;

    case MVGAL_NTSYNC_TYPE_TIMER:
        atomic_set(&obj->timer_expired, 0);
        break;

    case MVGAL_NTSYNC_TYPE_FENCE:
        /* Fence is created on-demand when signaled */
        obj->dma_fence = NULL;
        break;
    }

    spin_lock(&ntsync_lock);
    list_add_tail(&obj->node, &ntsync_objects);
    spin_unlock(&ntsync_lock);

    return obj;
}

/* ------------------------------------------------------------------ */
/*  File Operations                                                   */
/* ------------------------------------------------------------------ */

static int ntsync_open(struct inode *inode, struct file *file)
{
    file->private_data = NULL;
    return 0;
}

static int ntsync_release(struct inode *inode, struct file *file)
{
    struct mvgal_ntsync_object *obj = file->private_data;
    release_object(obj);
    return 0;
}

static __poll_t ntsync_poll(struct file *file, struct poll_table_struct *wait)
{
    struct mvgal_ntsync_object *obj = file->private_data;
    __poll_t mask = 0;

    if (!obj)
        return POLLERR;

    switch (obj->type) {
    case MVGAL_NTSYNC_TYPE_EVENT:
        poll_wait(file, &obj->waitq, wait);
        if (atomic_read(&obj->signaled))
            mask |= POLLIN | POLLRDNORM;
        break;

    case MVGAL_NTSYNC_TYPE_SEMAPHORE:
        poll_wait(file, &obj->waitq, wait);
        if (atomic_read(&obj->count) > 0)
            mask |= POLLIN | POLLRDNORM;
        break;

    case MVGAL_NTSYNC_TYPE_FENCE:
        poll_wait(file, &obj->waitq, wait);
        if (obj->dma_fence && dma_fence_is_signaled(obj->dma_fence))
            mask |= POLLIN | POLLRDNORM;
        break;

    default:
        mask = POLLIN | POLLOUT;
        break;
    }

    return mask;
}

/* ------------------------------------------------------------------ */
/*  IOCTL Handlers                                                      */
/* ------------------------------------------------------------------ */

static long ntsync_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    struct mvgal_ntsync_create_args create_args;
    struct mvgal_ntsync_wait_args wait_args;
    struct mvgal_ntsync_signal_args signal_args;
    struct mvgal_ntsync_fence_args fence_args;
    struct mvgal_ntsync_object *obj;
    __u32 type;
    int ret = 0;

    switch (cmd) {
    case MVGAL_NTSYNC_IOC_CREATE_EVENT:
        type = MVGAL_NTSYNC_TYPE_EVENT;
        goto create_regular;
    case MVGAL_NTSYNC_IOC_CREATE_SEMAPHORE:
        type = MVGAL_NTSYNC_TYPE_SEMAPHORE;
        goto create_regular;
    case MVGAL_NTSYNC_IOC_CREATE_MUTEX:
        type = MVGAL_NTSYNC_TYPE_MUTEX;
        goto create_regular;
    case MVGAL_NTSYNC_IOC_CREATE_TIMER:
        type = MVGAL_NTSYNC_TYPE_TIMER;
        goto create_regular;

create_regular:
        if (copy_from_user(&create_args, (void __user *)arg, sizeof(create_args)))
            return -EFAULT;

        obj = create_object(type, create_args.flags);
        if (!obj)
            return -ENOMEM;

        /* Set initial values for semaphores */
        if (type == MVGAL_NTSYNC_TYPE_SEMAPHORE) {
            atomic_set(&obj->count, create_args.initial);
            atomic_set(&obj->max_count, create_args.initial);
        }

        /* Create anon inode FD */
        ret = anon_inode_getfd("mvgal_ntsync", &ntsync_fops, obj,
                               O_RDWR | (create_args.flags & MVGAL_NTSYNC_FLAG_SHARED ? 0 : O_CLOEXEC));
        if (ret < 0) {
            release_object(obj);
            return ret;
        }

        create_args.type = type;
        create_args.fd = ret;

        if (copy_to_user((void __user *)arg, &create_args, sizeof(create_args)))
            return -EFAULT;

        atomic_inc(&obj->refcount);
        file->private_data = obj;
        return 0;

    case MVGAL_NTSYNC_IOC_CREATE_FENCE:
        if (copy_from_user(&fence_args, (void __user *)arg, sizeof(fence_args)))
            return -EFAULT;

        obj = create_object(MVGAL_NTSYNC_TYPE_FENCE, fence_args.flags);
        if (!obj)
            return -ENOMEM;

        /* For fences, store GPU index for cross-GPU integration. */
        obj->owner_tid = fence_args.gpu_index;

        /* Create anon inode FD */
        ret = anon_inode_getfd("mvgal_ntsync", &ntsync_fops, obj,
                               O_RDWR | (fence_args.flags & MVGAL_NTSYNC_FLAG_SHARED ? 0 : O_CLOEXEC));
        if (ret < 0) {
            release_object(obj);
            return ret;
        }

        fence_args.fd = ret;

        if (copy_to_user((void __user *)arg, &fence_args, sizeof(fence_args)))
            return -EFAULT;

        atomic_inc(&obj->refcount);
        file->private_data = obj;
        return 0;

    case MVGAL_NTSYNC_IOC_WAIT_EVENT:
    case MVGAL_NTSYNC_IOC_WAIT_SEMAPHORE:
    case MVGAL_NTSYNC_IOC_ACQUIRE_MUTEX:
    case MVGAL_NTSYNC_IOC_WAIT_FENCE:
        if (copy_from_user(&wait_args, (void __user *)arg, sizeof(wait_args)))
            return -EFAULT;

        obj = find_object_by_fd(wait_args.fd);
        if (!obj)
            return -ENOENT;

        atomic_inc(&obj->waiters);

        switch (obj->type) {
        case MVGAL_NTSYNC_TYPE_EVENT:
            if (wait_args.timeout_ns == 0) {
                wait_event(obj->waitq, atomic_read(&obj->signaled) != 0);
            } else {
                unsigned long timeout = nsecs_to_jiffies(wait_args.timeout_ns);
                if (!wait_event_timeout(obj->waitq,
                                       atomic_read(&obj->signaled) != 0,
                                       timeout)) {
                    wait_args.status = 1; /* Timed out */
                    atomic_dec(&obj->waiters);
                    release_object(obj);
                    goto out_copy_wait;
                }
            }
            wait_args.status = 0;
            if (obj->flags & MVGAL_NTSYNC_FLAG_AUTO_RESET) {
                atomic_set(&obj->signaled, 0);
            }
            break;

        case MVGAL_NTSYNC_TYPE_SEMAPHORE:
            while (atomic_read(&obj->count) == 0) {
                if (wait_args.timeout_ns == 0) {
                    wait_event(obj->waitq, atomic_read(&obj->count) > 0);
                } else {
                    unsigned long timeout = nsecs_to_jiffies(wait_args.timeout_ns);
                    if (!wait_event_timeout(obj->waitq,
                                           atomic_read(&obj->count) > 0,
                                           timeout)) {
                        wait_args.status = 1; /* Timed out */
                        atomic_dec(&obj->waiters);
                        release_object(obj);
                        goto out_copy_wait;
                    }
                }
            }
            atomic_dec(&obj->count);
            wait_args.status = 0;
            break;

        case MVGAL_NTSYNC_TYPE_MUTEX:
            if (wait_args.timeout_ns == 0) {
                wait_event(obj->waitq, atomic_read(&obj->signaled) != 0);
            } else {
                unsigned long timeout = nsecs_to_jiffies(wait_args.timeout_ns);
                if (!wait_event_timeout(obj->waitq,
                                       atomic_read(&obj->signaled) != 0,
                                       timeout)) {
                    wait_args.status = 1; /* Timed out */
                    atomic_dec(&obj->waiters);
                    release_object(obj);
                    goto out_copy_wait;
                }
            }
            atomic_set(&obj->signaled, 0);
            obj->owner_tid = current->pid;
            wait_args.status = 0;
            break;

        case MVGAL_NTSYNC_TYPE_FENCE:
            /* For cross-GPU fences, we need to wait on the DMA fence */
            if (obj->dma_fence) {
                if (dma_fence_wait_timeout(obj->dma_fence, true,
                                          nsecs_to_jiffies(wait_args.timeout_ns))) {
                    wait_args.status = 0;
                } else {
                    wait_args.status = 1; /* Timed out */
                }
            } else {
                wait_args.status = 0; /* No fence = already signaled */
            }
            break;
        }

        atomic_dec(&obj->waiters);
        release_object(obj);
        break;

    case MVGAL_NTSYNC_IOC_SIGNAL_EVENT:
    case MVGAL_NTSYNC_IOC_RELEASE_SEMAPHORE:
    case MVGAL_NTSYNC_IOC_RELEASE_MUTEX:
    case MVGAL_NTSYNC_IOC_SIGNAL_FENCE:
        if (copy_from_user(&signal_args, (void __user *)arg, sizeof(signal_args)))
            return -EFAULT;

        obj = find_object_by_fd(signal_args.fd);
        if (!obj)
            return -ENOENT;

        switch (obj->type) {
        case MVGAL_NTSYNC_TYPE_EVENT:
            atomic_set(&obj->signaled, 1);
            wake_up_all(&obj->waitq);
            break;

        case MVGAL_NTSYNC_TYPE_SEMAPHORE:
            atomic_inc(&obj->count);
            if (atomic_read(&obj->count) > atomic_read(&obj->max_count)) {
                atomic_set(&obj->count, atomic_read(&obj->max_count));
            }
            wake_up_all(&obj->waitq);
            break;

        case MVGAL_NTSYNC_TYPE_MUTEX:
            atomic_set(&obj->signaled, 1); /* Unlocked */
            obj->owner_tid = 0;
            wake_up_all(&obj->waitq);
            break;

        case MVGAL_NTSYNC_TYPE_FENCE:
            if (obj->dma_fence)
                dma_fence_signal(obj->dma_fence);
            wake_up_all(&obj->waitq);
            break;
        }

        release_object(obj);
        return 0;

    default:
        return -ENOTTY;
    }

out_copy_wait:
    if (copy_to_user((void __user *)arg, &wait_args, sizeof(wait_args)))
        return -EFAULT;

    return ret;
}

/* ------------------------------------------------------------------ */
/*  File Operations Structure                                           */
/* ------------------------------------------------------------------ */

static const struct file_operations ntsync_fops = {
    .owner          = THIS_MODULE,
    .open           = ntsync_open,
    .release        = ntsync_release,
    .poll           = ntsync_poll,
    .unlocked_ioctl = ntsync_ioctl,
    .compat_ioctl   = ntsync_ioctl,
};

/* ------------------------------------------------------------------ */
/*  Misc Device                                                       */
/* ------------------------------------------------------------------ */

static struct miscdevice ntsync_miscdev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name  = "mvgal_ntsync",
    .fops  = &ntsync_fops,
};

/* ------------------------------------------------------------------ */
/*  Module Entry/Exit                                                 */
/* ------------------------------------------------------------------ */

static int __init ntsync_init(void)
{
    int ret;

    pr_info("MVGAL NTSYNC module loaded (v%s)\n", MVGAL_NTSYNC_VERSION);

    ret = misc_register(&ntsync_miscdev);
    if (ret) {
        pr_err("Failed to register misc device: %d\n", ret);
        return ret;
    }

    pr_info("MVGAL NTSYNC: /dev/mvgal_ntsync ready\n");
    return 0;
}

static void __exit ntsync_exit(void)
{
    struct mvgal_ntsync_object *obj, *tmp;

    spin_lock(&ntsync_lock);
    list_for_each_entry_safe(obj, tmp, &ntsync_objects, node) {
        list_del(&obj->node);
        if (obj->dma_fence)
            dma_fence_put(obj->dma_fence);
        kfree(obj);
    }
    spin_unlock(&ntsync_lock);

    misc_deregister(&ntsync_miscdev);
    pr_info("MVGAL NTSYNC module unloaded\n");
}

module_init(ntsync_init);
module_exit(ntsync_exit);

MODULE_AUTHOR("MVGAL Team");
MODULE_DESCRIPTION("MVGAL NT Synchronization Primitives for Wine/Proton");
MODULE_LICENSE("GPL v2");
MODULE_VERSION(MVGAL_NTSYNC_VERSION);
