/**
 * @file mydev.c
 * @brief Simple character device driver example
 *
 * This module demonstrates a basic char device driver
 * with open, read, and write operations.
 */

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/mutex.h>

#define DEVICE_NAME "mydev"
#define BUFFER_SIZE 128

static int major;
static char buffer[BUFFER_SIZE] = {0};
static size_t data_size = 0;

static int device_opened = 0;
static DEFINE_MUTEX(mydev_lock);

/**
 * @brief Open device
 */
static int my_open(struct inode *inode, struct file *file)
{
    int err_code = 0;

    mutex_lock(&mydev_lock);

    if (device_opened) {
        printk(KERN_WARNING "%s: device busy\n", DEVICE_NAME);
        err_code = -EBUSY;
        goto ret_state;
    }

    device_opened = 1;
    printk(KERN_INFO "%s: device opened successfully\n", DEVICE_NAME);

ret_state:
    mutex_unlock(&mydev_lock);
    return err_code;
}

/**
 * @brief Release device
 */
static int my_release(struct inode *inode, struct file *file)
{
    mutex_lock(&mydev_lock);

    device_opened = 0;
    printk(KERN_INFO "%s: device released\n", DEVICE_NAME);

    mutex_unlock(&mydev_lock);
    return 0;
}

/**
 * @brief Read from device
 */
static ssize_t my_read(struct file *file, char __user *user_buf,
                       size_t len, loff_t *offset)
{
    int bytes_read;

    if (*offset >= data_size)
        return 0;

    bytes_read = min(len, data_size - (size_t)*offset);

    if (copy_to_user(user_buf, buffer + *offset, bytes_read))
        return -EFAULT;

    *offset += bytes_read;

    // printk(KERN_INFO "mydev: read %d bytes\n", bytes_read);
    printk(KERN_INFO "mydev: read %d bytes, offset=%lld\n",
       bytes_read, *offset);

    return bytes_read;
}

/**
 * @brief Write to device
 */
static ssize_t my_write(struct file *file, const char __user *user_buf,
                        size_t len, loff_t *offset)
{
    int bytes_write;

    bytes_write = min(len, (size_t)BUFFER_SIZE);

    if (copy_from_user(buffer, user_buf, bytes_write))
        return -EFAULT;

    data_size = bytes_write;

    // printk(KERN_INFO "mydev: write %d bytes\n", bytes_write);
    printk(KERN_INFO "mydev: write %d bytes, data: %.*s\n",
       bytes_write, bytes_write, buffer);



    return bytes_write;
}

static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = my_open,
    .release = my_release,
    .read = my_read,
    .write = my_write,
};

/**
 * @brief Module init
 */
static int __init my_init(void)
{
    major = register_chrdev(0, DEVICE_NAME, &fops);

    if (major < 0) {
        printk(KERN_ALERT "mydev: failed to register\n");
        return major;
    }

    printk(KERN_INFO "mydev: registered with major %d\n", major);
    return 0;
}

/**
 * @brief Module exit
 */
static void __exit my_exit(void)
{
    unregister_chrdev(major, DEVICE_NAME);
    printk(KERN_INFO "mydev: unregistered\n");
}

module_init(my_init);
module_exit(my_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("CWChien");
MODULE_DESCRIPTION("Simple Char Device");

// END OF FILE
