/*-----------------------------------------------------------------------------------------------*\
\*-----------------------------------------------------------------------------------------------*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#include <linux/config.h>
#include <linux/slab.h>
#include <asm/fcntl.h>
#include <asm/ioctl.h>
#include <linux/fs.h>
#include <asm/semaphore.h>
#include <asm/errno.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/vmalloc.h>
#define AVM_EVENT_INTERNAL
#include <linux/avm_event.h>
#include <asm/atomic.h>
#include <asm/mach_avm.h>

#define CONFIG_AVM_PUSH_BUTTON_GPIO (8)
#define AVM_EVENT_PUSH_BUTTON_SAMPLE_TIME       250
/*--- #define AVM_EVENT_PUSH_BUTTON_SAMPLE_TIME       5000 ---*/
#define MSEC(x)                                 ((HZ * (x)) / 1000)

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static struct _avm_event_push_button_state {
    void *handle;
    unsigned long long jiffies_64;
    unsigned int value;
    struct timer_list timer;
} Button;

extern int avm_event_enable_push_button;

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
#if 0
static void blabla(char* text) {
    volatile unsigned int *gpio_ctrl = (unsigned int*)AVALANCHE_GPIO_ENBL;
    volatile unsigned int *gpio_dir  = (unsigned int*)AVALANCHE_GPIO_DIR;
    volatile unsigned int *gpio_in  = (unsigned int*)AVALANCHE_GPIO_DATA_IN;
    unsigned int state;
    state = avalanche_gpio_in_bit(CONFIG_AVM_PUSH_BUTTON_GPIO) ? avm_event_push_button_gpio_low : avm_event_push_button_gpio_high;
    printk("[%s] gpio_ctrl=0x%x with mask 0x%x 0x%x\n", text, *gpio_ctrl, 1 << CONFIG_AVM_PUSH_BUTTON_GPIO, *gpio_ctrl & (1 << CONFIG_AVM_PUSH_BUTTON_GPIO));
    printk("[%s] gpio_dir=0x%x with mask 0x%x 0x%x\n", text, *gpio_dir, 1 << CONFIG_AVM_PUSH_BUTTON_GPIO, *gpio_dir & (1 << CONFIG_AVM_PUSH_BUTTON_GPIO));
    printk("[%s] gpio_in=0x%x with mask 0x%x 0x%x\n", text, *gpio_in, 1 << CONFIG_AVM_PUSH_BUTTON_GPIO, *gpio_in & (1 << CONFIG_AVM_PUSH_BUTTON_GPIO));
    printk("[%s] GPIO %u is %s ", text, CONFIG_AVM_PUSH_BUTTON_GPIO, state ? "avm_event_push_button_gpio_high" : "avm_event_push_button_gpio_low");
    printk("[%s] Button.value %s\n", text, Button.value ? "avm_event_push_button_gpio_high" : "avm_event_push_button_gpio_low");
}
#endif

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
void avm_event_push_button_timer_handler(unsigned long Data) {
    unsigned int state;

    del_timer(&(Button.timer));
    state = avm_gpio_in_bit(CONFIG_AVM_PUSH_BUTTON_GPIO) ? avm_event_push_button_gpio_low : avm_event_push_button_gpio_high;
    /*--- blabla("avm_event_push_button_timer_handler"); ---*/

    /*--- new button pressed ---*/
    if(state == avm_event_push_button_gpio_high && Button.value == avm_event_push_button_gpio_low) {
        /*--- printk("[A]"); ---*/
        Button.jiffies_64 = jiffies_64;
        Button.value = avm_event_push_button_gpio_high;
        printk("Button presses: GPIO=%u time=%u\n", CONFIG_AVM_PUSH_BUTTON_GPIO, (unsigned int)Button.jiffies_64);
    }
    /*--- new button released ---*/
    if(state == avm_event_push_button_gpio_low && Button.value == avm_event_push_button_gpio_high) {
        struct _avm_event_push_button *event;
        /*--- printk("[B]"); ---*/
        event = (struct _avm_event_push_button *)kmalloc(sizeof(struct _avm_event_push_button), GFP_ATOMIC);
        if(event) {
            event->id      = avm_event_id_push_button;
            event->pressed = (unsigned int)(jiffies_64 - Button.jiffies_64);
            if(event->pressed < MSEC(2000)) {
                event->key = avm_event_push_button_key_1;
            } else if(event->pressed < MSEC(8000)) {
                event->key = avm_event_push_button_key_2;
            } else {
                event->key = avm_event_push_button_key_3;
            }
            event->pressed *= 10;

            Button.value = avm_event_push_button_gpio_low;
            avm_event_source_trigger(Button.handle, avm_event_id_push_button, sizeof(struct _avm_event_push_button), event);
            printk("Button released: GPIO=%u presses=%u key=%s\n", 
                    CONFIG_AVM_PUSH_BUTTON_GPIO, 
                    (unsigned int)event->pressed,
                    event->key == avm_event_push_button_key_1 ? "avm_event_push_button_key_1" :
                    event->key == avm_event_push_button_key_2 ? "avm_event_push_button_key_2" :
                    event->key == avm_event_push_button_key_3 ? "avm_event_push_button_key_3" : "unknown"
                );
        }
    }
    Button.timer.function = avm_event_push_button_timer_handler;
    Button.timer.data     = -1;
    Button.timer.expires  = jiffies + MSEC(AVM_EVENT_PUSH_BUTTON_SAMPLE_TIME);
    add_timer(&(Button.timer));
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
void avm_event_push_button_notify(void *Context, enum _avm_event_id id) {
    struct _avm_event_push_button *event;
    event = (struct _avm_event_push_button *)kmalloc(sizeof(struct _avm_event_push_button), GFP_ATOMIC);
    if(event == NULL)
        return;

    event->id      = avm_event_id_push_button;
    event->key     = avm_gpio_in_bit(CONFIG_AVM_PUSH_BUTTON_GPIO) ? avm_event_push_button_gpio_low : avm_event_push_button_gpio_high;
    event->pressed = 0;

    avm_event_source_trigger(Button.handle, avm_event_id_push_button, sizeof(struct _avm_event_push_button), event);
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
void avm_event_push_button_init(void) {

    if(avm_event_enable_push_button == 0)
        return;
    avm_gpio_ctrl(CONFIG_AVM_PUSH_BUTTON_GPIO, GPIO_PIN, GPIO_INPUT_PIN);
    Button.handle         = avm_event_source_register("push_button", (unsigned long long)1 << avm_event_id_push_button,avm_event_push_button_notify, (void *)0);
    Button.value          = avm_event_push_button_gpio_low;
    Button.jiffies_64     = jiffies_64;

	init_timer(&(Button.timer));
    Button.timer.function = avm_event_push_button_timer_handler;
    Button.timer.data     = -1;
    Button.timer.expires  = jiffies + MSEC(AVM_EVENT_PUSH_BUTTON_SAMPLE_TIME);
    add_timer(&(Button.timer));

    /*--- blabla("avm_event_push_button_init"); ---*/
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
void avm_event_push_button_deinit(void) {
    if(avm_event_enable_push_button == 0)
        return;
    del_timer(&Button.timer);
    avm_event_source_release(Button.handle);
}
