/***************************************************************************/
/*                             Druckersoftware                             */
/*                    Copyright (C) Carl Valentin GmbH                     */
/***************************************************************************/

/*
 * temp_test.h
 *
 *  Created on: 08.04.2022
 *      Author: preitinger
 */

#ifndef TEMP_TEST_H_
#define TEMP_TEST_H_


#include <linux/fs.h>

#define CV_TEMP_CLASS_NAME "CV_TEMP"
#define CV_DEV_NAME "cv_temp"

static struct class* cv_temp_class = NULL;
static MyDevice cv_temp_dev;

static bool cv_temp_dev_deleted = false;


static int cv_temp_open(struct inode * n, struct file * f) {
    pr_info("cv_temp_open\n");
    return 0;
}

static int cv_temp_release(struct inode * n, struct file * f) {
    pr_info("cv_temp_release\n");
    pr_info("vor ssleep(10) in release\n");
    ssleep(10);
    pr_info("nach ssleep in release\n");
    return 0;

    // Test-Fazit:
    // Selbst wenn der Prozess mit offenem File Handle noch so schlimm "abstuerzt" (auch kill -9)
    // alle relase-Aufrufe werden sauber zuende ausgefuehrt vom kernel bevor der Prozess wirklich beendet wird.
}

static ssize_t cv_temp_write(struct file *file, const char __user *buf, size_t count,
              loff_t *ppos) {
#if 0
    cv_temp_dev_deleted = true;
    pr_info("cv_temp_write: vor MyDevice_delete\n");
    MyDevice_delete(&cv_temp_dev);
    pr_info("cv_temp_write: nach MyDevice_delete\n");
#endif

    return count;
}

static ssize_t cv_temp_read(struct file* f, char __user* buf, size_t count, loff_t *ppos) {
    pr_info("cv_temp_read vor ssleep\n");
    ssleep(2); // einfach lang blockieren als test
    pr_info("cv_temp_read nach ssleep\n");
    {
        unsigned long notCopied;
        char bla = 42;

        notCopied = copy_to_user(buf, &bla, 1);
        return 1 - notCopied;
    }
}

static struct file_operations cv_temp_ops =
{
        .open = cv_temp_open,
        .release = cv_temp_release,
        .write = cv_temp_write,
        .read = cv_temp_read,
};

int temp_test_init(void) {
    int res = 0;

    cv_temp_class = class_create(THIS_MODULE, CV_TEMP_CLASS_NAME);

    if (IS_ERR(cv_temp_class)) {
        res = PTR_ERR(cv_temp_class);
        goto err_class;
    }

    cv_temp_dev_deleted = false;

    if (MyDevice_create(&cv_temp_dev, cv_temp_class, &cv_temp_ops, "%s", CV_DEV_NAME)) {
        // nicht 0, also fehler
        res = -EIO;
        goto err_my_device;
    }

    ////////////
    return 0;
    ////////////

    err_my_device:

    class_destroy(cv_temp_class);

    err_class:

    return res;
}

void temp_test_exit(void) {

    if (!cv_temp_dev_deleted) {
        pr_info("temp_test_exit vor MyDevice_delete\n");
        MyDevice_delete(&cv_temp_dev);
    } else {
        pr_info("temp_test_exit skip MyDevice_delete\n");
    }

    pr_info("temp_test_exit vor class_destroy\n");
    class_destroy(cv_temp_class);
}

#endif /* TEMP_TEST_H_ */
