/******************************************************************************
 * FILE PURPOSE:     - LED HAL module source
 ******************************************************************************
 * FILE NAME:     led_hal.c
 *
 * DESCRIPTION:    LED HAL API's source.
 *
 * REVISION HISTORY:
 * 27 Aug 2003    Initial Creation                      Sharath Kumar<krs@ti.com>  
 * 
 * 16 Dec 2003    Updates for 5.7                       Sharath Kumar<krs@ti.com>       
 *
 * 07 Jan 2004    Wrapper for DSL                       Sharath Kumar<krs@ti.com>                                                   
 *
 * (C) Copyright 2002, Texas Instruments, Inc
 *******************************************************************************/

#include <asm/semaphore.h>
#include "led_platform.h"
#include <asm/mach-ohio/led_config.h>
#include "led_hal.h"
#if defined(CONFIG_MIPS_OHIO)
#include <asm/mips-boards/ohio.h>
#include <asm/mach_avm.h>
#include <linux/avm_event.h>
#include <asm/uaccess.h>
#include <linux/module.h>
#endif /*--- #if defined(CONFIG_MIPS_OHIO) ---*/

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
#if 0
#define DEB_EVENT(args...)     printk(KERN_ERR args)
#else
#define DEB_EVENT(args...)     
#endif

#define REQUIRES_TIMER(x) ((x == LED_ONESHOT_ON) || (x == LED_ONESHOT_OFF) || (x == LED_FLASH) || (x == LED_BLINK_CODE0) || (x == LED_BLINK_CODE1) || (x == LED_BLINK_CODE2))


/*******************TYPEDEFs**************************************************/
typedef struct
{
    unsigned int state[MAX_MODULE_INSTANCES];
    unsigned int module_map;
    unsigned int pos_map[2];
}
LED_OBJ_T;

typedef struct state_entry STATE_ENTRY_T;

struct state_entry
{
    void (*handler) (STATE_ENTRY_T * state);
    unsigned int timer_running;
    LED_OBJ_T *led;
    unsigned int map1[2];
    unsigned int map2[2];
    void *os_timer;
    unsigned int param1;
    unsigned int param2;
    unsigned int module_id;
    unsigned int instance;
    unsigned int mode;
};

typedef struct module_instance
{
    int module_id;
    int instance;
    struct _module_instance_event {
        int id;
        unsigned int buffer_len;
        void *buffer;
    } event;
    STATE_ENTRY_T *states[MAX_STATE_ENTRIES];
}
MODULE_INSTANCE_T;

typedef struct module_entry
{
    unsigned char *name;
    MODULE_INSTANCE_T *module_instance[MAX_MODULE_INSTANCES];
}
MODULE_ENTRY_T;

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
enum _led_blink_state {
    led_blink_0_state_next_on_0,

    led_blink_1_state_next_on_0,
    led_blink_1_state_next_off_0,
    led_blink_1_state_next_on_1,
    
    led_blink_2_state_next_on_0,
    led_blink_2_state_next_off_0,
    led_blink_2_state_next_on_1,
    led_blink_2_state_next_short_pause,
    led_blink_2_state_next_on_2,

    led_blink_0_state_next_pause,
    led_blink_1_state_next_pause,
    led_blink_2_state_next_pause
};

char *_led_blink_state_name[] = {
    "led_blink_0_state_next_on_0",
    "led_blink_1_state_next_on_0",
    "led_blink_1_state_next_off_0",
    "led_blink_1_state_next_on_1",
    "led_blink_2_state_next_on_0",
    "led_blink_2_state_next_off_0",
    "led_blink_2_state_next_on_1",
    "led_blink_2_state_next_short_pause",
    "led_blink_2_state_next_on_2",
    "led_blink_0_state_next_pause"
    "led_blink_1_state_next_pause"
    "led_blink_2_state_next_pause"
};

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
#define MAX_LATE_ACTIONS        20
static unsigned int avalanche_led_configurated = 0;
static struct _avalanche_led_late_action {
    void *handle;
    int state;
} avalanche_led_late_action[MAX_LATE_ACTIONS];


/******************variable defn/declns***************************************/

static LED_OBJ_T *leds[MAX_LED_ENTRIES];
static MODULE_ENTRY_T *modules[MAX_MODULE_ENTRIES];

/* Declare Mutex lock */
spinlock_t avm_led_lock = SPIN_LOCK_UNLOCKED;

/*  GPIO OFF STATE  */
static unsigned int gpio_offstate_map[2];

/*  Number of GPIO pins in the system  */
static unsigned int num_gpios;

/* LED handlers */
static void (*led_mode_handler[NUM_LED_MODES]) (STATE_ENTRY_T * state);



/******************static functions*****************************************/

static void *led_malloc (int n) {
    void *p;
    p = os_malloc (n);

    if (p)
        os_memset (p, 0, n);

    return p;
}

unsigned led_state[2][MAX_MODULE_INSTANCES][32];  /*--- fuer 64 LEDs ---*/

/*------------------------------------------------------------------------------------------*\
 * STATE_ENTRY_T * state
\*------------------------------------------------------------------------------------------*/
static void avalanche_gpio_set(int *set_map, int *pos_map, STATE_ENTRY_T *state) {
    
    unsigned int output_set_map;
    int i, index;

#if 0
    int i;
    for(i = 0; i < num_gpios ; i += 32) {
        int index = i/32;
        /*--- DEB_INFO("[avalanche_led/avalanche_gpio_set]:  set mask 0x%x clear mask 0x%x index %u\n", set_map[index], pos_map[index], index); ---*/
        avalanche_gpio_out_value(set_map[index], pos_map[index],index);
        
    } 
#endif
    DEB_INFO("avalanche_gpio_set(set: 0x%x pos: 0x%x id=%u)\n", set_map[0], pos_map[0], state->module_id);

    /*--------------------------------------------------------------------------------------*\
    \*--------------------------------------------------------------------------------------*/
#if 0
    if(state->instance == 0) {
        for( i = 0 ; i < MAX_MODULE_ENTRIES ; i++) {
            int ii;
            if(modules[i] == 0)
                continue;
            for(ii = 0 ; ii < MAX_MODULE_INSTANCES ; ii++) {
                int iii;
                if(modules[i]->module_instance[ii] == 0)
                    continue;
                for( iii = 0 ; iii < MAX_STATE_ENTRIES ; iii++) {
                    if(modules[i]->module_instance[ii]->states[iii] == state) {
                        DEB_INFO("[%s] module_id %u instance %u\n",
                            (char *)(modules[i]->name ?  (char *)modules[i]->name : "no name"), 
                            modules[i]->module_instance[ii]->module_id,
                            modules[i]->module_instance[ii]->instance);
                        state->instance = modules[i]->module_instance[ii]->instance + 1;
                        goto found;
                    }
                }
            }
        }
    }
found:
#endif

    /*--------------------------------------------------------------------------------------*\
     * fuer alle möglichen LEDs
    \*--------------------------------------------------------------------------------------*/
    for(index = 0 ; index < (num_gpios / 32) ; index++) {
        unsigned int bit_pos = state->module_id;
        output_set_map = 0;
        if(bit_pos >= 32) {
            DEB_INFO("[avalanche_gpio_set] ERROR: led_state Bit mask to large %u\n", bit_pos);
            return;
        }

        for(i = 0 ; i < 32 ; i++) {
            int ii;
            /*----------------------------------------------------------------------------------*\
             * fuer jede LED die in pos_map gesetzt ist das zur Instanz gehörende Bit in 
             * led_state löschen
            \*----------------------------------------------------------------------------------*/
            if(pos_map[index] & (1 << i)) {
                led_state[index][state->instance][i] &= ~(1 << bit_pos);
            } else {
                continue;
            }
            /*----------------------------------------------------------------------------------*\
             * fuer jede LED die in set_map gesetzt ist das zur Instanz gehörende Bit in 
             * led_state setzen
            \*----------------------------------------------------------------------------------*/
            if((set_map[index] & (1 << i)) == 0) {
                led_state[index][state->instance][i] |=  (1 << bit_pos);
            }
            /*----------------------------------------------------------------------------------*\
             * das Bit in der output set mask setzen wenn irgend ein Bit in dem entsprechenden
             * led_state gesetzt ist.
            \*----------------------------------------------------------------------------------*/
            for(ii = 0 ; ii < MAX_MODULE_INSTANCES ; ii++) {
                if(led_state[index][ii][i]) {
                    output_set_map |= 1 << i;
                    break;
                }
            }

        }
        output_set_map = ~output_set_map & pos_map[index];

        DEB_INFO("gpio_set(output: 0x%x pos: 0x%x id=%u, index=%u)\n", output_set_map, pos_map[index], state->module_id, index);
#if defined(CONFIG_AVM_UBIK2)
        ubik2_ioarea_systemservices->GPIO.GPOUT[index] &= ~pos_map[index];
        ubik2_ioarea_systemservices->GPIO.GPOUT[index] |= output_set_map & pos_map[index];
#endif /*--- #if defined(CONFIG_AVM_UBIK2) ---*/
#if defined(CONFIG_MIPS_OHIO)
        /*--- *GPIO_OUT &=  ~pos_map[index]; ---*/
        /*--- *GPIO_OUT |= output_set_map & pos_map[index]; ---*/
        avm_gpio_set_bitmask(pos_map[index], output_set_map); 
#endif /*--- #if defined(CONFIG_MIPS_OHIO) ---*/

    }
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static MODULE_INSTANCE_T *get_module (char *name, int instance)
{
    int module_id;
    MODULE_INSTANCE_T *mod_inst;

    DEB_INFO("[avalanche_led/get_module]:  find: name=%s instance=%u\n", name, instance);

    if (instance >= MAX_MODULE_INSTANCES) {
        DEB_ERR("[avalanche_led/get_module]: instance > MAX_MODULE_INSTANCES (%u)\n", MAX_MODULE_INSTANCES);
        return NULL;
    }

    for (module_id = 0; module_id < MAX_MODULE_ENTRIES; module_id++) {
        if (modules[module_id] && !os_strcmp (name, modules[module_id]->name)) {
            DEB_INFO("[avalanche_led/get_module]:  module found module_id=%u name=%s\n", module_id, name);
            break;
        }
    }

    if (module_id == MAX_MODULE_ENTRIES) {
        for (module_id = 0; (module_id < MAX_MODULE_ENTRIES) && modules[module_id]; module_id++)
            ;

        if (module_id < MAX_MODULE_ENTRIES) {
            modules[module_id] = led_malloc (sizeof (MODULE_ENTRY_T));

            if (!modules[module_id])  {
                DEB_ERR("[avalanche_led/get_module]: module (%u) not valid\n", module_id);
                return NULL;
            }

            modules[module_id]->name = led_malloc (os_strlen (name) + 1);

            if (!modules[module_id]->name) {
                DEB_ERR("[avalanche_led/get_module]: module (%u) has no name\n", module_id);
                return NULL;
            }

            os_strcpy (modules[module_id]->name, name);
        } else {
            log_msg ("ERROR:Module Count exceeded\n");
            return NULL;
        }
    }

    if (!modules[module_id]->module_instance[instance]) {
        modules[module_id]->module_instance[instance] = led_malloc (sizeof (MODULE_INSTANCE_T));
    }

    if (!modules[module_id]->module_instance[instance]) {
        DEB_ERR("[avalanche_led/get_module]: instance (%u) not valid\n", instance);
        return NULL;
    }

    mod_inst = modules[module_id]->module_instance[instance];
    mod_inst->module_id = module_id;
    mod_inst->instance = instance;

    DEB_INFO("[avalanche_led/get_module]:  handle=0x%p\n", (void *)mod_inst);
    return mod_inst;
}


/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static void assign_map(int *to, int *from)
{
    int i;
    
    for(i = 0 ; i < num_gpios ; i += 32) { 
        int index = i/32;
        to[index] = from[index];
    }
} 




/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static LED_OBJ_T *get_led (int * pos_map)
{
    int led_id;

    for (led_id = 0; led_id < MAX_LED_ENTRIES ; led_id++) {
        if (leds[led_id]) {
            int i;
            int flag=0;
    
            for(i = 0 ; i < num_gpios ; i += 32) { 
                int index = i/32;
                if(leds[led_id]->pos_map[index] != pos_map[index])
                    flag =1;
            } 
            if(flag == 0)
                break;
        }
    }

    if (led_id == MAX_LED_ENTRIES) {
        for (led_id = 0 ; (led_id < MAX_LED_ENTRIES) && leds[led_id] ; led_id++)
            ;

        if (led_id < MAX_LED_ENTRIES) {
            leds[led_id] = led_malloc (sizeof (LED_OBJ_T));

            if (!leds[led_id])
                return NULL;

            assign_map(leds[led_id]->pos_map, pos_map);
        } else {
            log_msg ("ERROR:Module Count exceeded\n");
            return NULL;
        }
    }
    return leds[led_id];
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static void led_oneshot_on_timer_func (int arg) {
    STATE_ENTRY_T *state = (STATE_ENTRY_T *) arg;
    LED_OBJ_T *led = state->led;
    unsigned long flags;

    spin_lock_irqsave(&avm_led_lock, flags);
    state->timer_running = 0;
    led->state[state->instance] = LED_OFF;
    avalanche_gpio_set(state->map2, led->pos_map, state);
    spin_unlock_irqrestore(&avm_led_lock, flags);

}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static void led_oneshot_off_timer_func (int arg) {
    STATE_ENTRY_T *state = (STATE_ENTRY_T *) arg;
    LED_OBJ_T *led = state->led;
    unsigned long flags;

    spin_lock_irqsave(&avm_led_lock, flags);
    state->timer_running = 0;
    led->state[state->instance] = LED_ON;
    avalanche_gpio_set(state->map2, led->pos_map, state);
    spin_unlock_irqrestore(&avm_led_lock, flags);

}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static void led_flash_timer_func (int arg) {
    STATE_ENTRY_T *state = (STATE_ENTRY_T *) arg;
    LED_OBJ_T *led = state->led;
    unsigned long flags;

    spin_lock_irqsave(&avm_led_lock, flags);

    if (led->state[state->instance] != LED_FLASH) {
        state->timer_running = 0;
        spin_unlock_irqrestore(&avm_led_lock, flags);
        DEB_INFO("[led_flash_timer_func] instance = %u state (%u) != LED_FLASH: stop timer\n", state->instance, led->state[state->instance]);
        return;
    }

    if (state->timer_running == 1) {
          state->timer_running = 2;
          avalanche_gpio_set(state->map2, led->pos_map, state);
          os_timer_add (state->os_timer, state->param2, (int)state);
    } else {
          state->timer_running = 1;
          avalanche_gpio_set(state->map1, led->pos_map, state);
          os_timer_add (state->os_timer, state->param1, (int)state);
    }

    spin_unlock_irqrestore(&avm_led_lock, flags);
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
#define set_blink_state(text, var, value)     { DEB_INFO("[set_blink_state]: %s %s->%s\n", text, _led_blink_state_name[var], _led_blink_state_name[value]); (var) = (value); }


static void led_blink_code_timer_func (int arg) {
    STATE_ENTRY_T *state = (STATE_ENTRY_T *) arg;
    LED_OBJ_T *led = state->led;
    enum _led_blink_state blink_state =  (state->timer_running >> 1) & 0xFF;
    unsigned int blink_count          = state->timer_running >> 9;
    unsigned long flags;

    spin_lock_irqsave(&avm_led_lock, flags);

    switch(blink_state) {
        case led_blink_0_state_next_on_0:
        case led_blink_0_state_next_pause:
            if(led->state[state->instance] != LED_BLINK_CODE0) 
                state->timer_running = 0;
            break;

        case led_blink_1_state_next_on_0:
        case led_blink_1_state_next_off_0:
        case led_blink_1_state_next_on_1:
        case led_blink_1_state_next_pause:
            if(led->state[state->instance] != LED_BLINK_CODE1) 
                state->timer_running = 0;
            break;
    
        case led_blink_2_state_next_on_0:
        case led_blink_2_state_next_off_0:
        case led_blink_2_state_next_on_1:
        case led_blink_2_state_next_short_pause:
        case led_blink_2_state_next_on_2:
        case led_blink_2_state_next_pause:
            if(led->state[state->instance] != LED_BLINK_CODE2) 
                state->timer_running = 0;
            break;
    }
    if(state->timer_running == 0) {
        spin_unlock_irqrestore(&avm_led_lock, flags);
        DEB_INFO("[led_blink_code_timer_funcX] instance = %u state (%u) != LED_LED_BLINK_CODE[012]: stop timer\n", state->instance, led->state[state->instance]);
        return;
    }

    /*--------------------------------------------------------------------------------------*\
    \*--------------------------------------------------------------------------------------*/
    switch(blink_state) {
        /*----------------------------------------------------------------------------------*\
        \*----------------------------------------------------------------------------------*/
        case led_blink_0_state_next_on_0:
            avalanche_gpio_set(state->map1, led->pos_map, state); /*--- einschalten ---*/
            os_timer_add (state->os_timer, state->param1, (int)state);
            set_blink_state("(handle led_blink_0_state_next_on_0)", blink_state, led_blink_0_state_next_pause);
            break;
        /*----------------------------------------------------------------------------------*\
        \*----------------------------------------------------------------------------------*/
        case led_blink_1_state_next_on_0:
            avalanche_gpio_set(state->map1, led->pos_map, state); /*--- einschalten ---*/
            os_timer_add (state->os_timer, state->param1 / 4, (int)state);
            set_blink_state("(handle led_blink_1_state_next_on_0)", blink_state, led_blink_1_state_next_off_0);
            break;
        case led_blink_1_state_next_off_0:
            avalanche_gpio_set(state->map2, led->pos_map, state);  /*--- ausschalten ---*/
            os_timer_add (state->os_timer, state->param1 / 4, (int)state);
            set_blink_state("(handle led_blink_1_state_next_off_0)", blink_state, led_blink_1_state_next_on_1);
            break;
        case led_blink_1_state_next_on_1:
            avalanche_gpio_set(state->map1, led->pos_map, state); /*--- einschalten ---*/
            os_timer_add (state->os_timer, state->param1 / 4, (int)state);
            set_blink_state("(handle led_blink_1_state_next_on_1)", blink_state, led_blink_1_state_next_pause);
            break;
        /*----------------------------------------------------------------------------------*\
        \*----------------------------------------------------------------------------------*/
        case led_blink_2_state_next_on_0:
            avalanche_gpio_set(state->map1, led->pos_map, state); /*--- einschalten ---*/
            os_timer_add (state->os_timer, state->param1 / 4, (int)state);
            set_blink_state("(handle led_blink_2_state_next_on_0)", blink_state, led_blink_2_state_next_off_0);
            break;
        case led_blink_2_state_next_off_0:
            avalanche_gpio_set(state->map2, led->pos_map, state);  /*--- ausschalten ---*/
            os_timer_add (state->os_timer, state->param1 / 4, (int)state);
            set_blink_state("(handle led_blink_2_state_next_off_0)", blink_state, led_blink_2_state_next_on_1);
            break;
        case led_blink_2_state_next_on_1:
            avalanche_gpio_set(state->map1, led->pos_map, state); /*--- einschalten ---*/
            os_timer_add (state->os_timer, state->param1 / 4, (int)state);
            set_blink_state("(handle led_blink_2_state_next_on_1)", blink_state, led_blink_2_state_next_short_pause);
            break;
        case led_blink_2_state_next_short_pause:
            avalanche_gpio_set(state->map2, led->pos_map, state);  /*--- ausschalten ---*/
            os_timer_add (state->os_timer, state->param1 * 2, (int)state);
            set_blink_state("(handle led_blink_2_state_next_short_pause)", blink_state, led_blink_2_state_next_on_2);
            break;
        case led_blink_2_state_next_on_2:
            avalanche_gpio_set(state->map1, led->pos_map, state); /*--- einschalten ---*/
            os_timer_add (state->os_timer, state->param1, (int)state);
            set_blink_state("(handle led_blink_2_state_next_on_2)", blink_state, led_blink_2_state_next_pause);
            break;
        /*----------------------------------------------------------------------------------*\
        \*----------------------------------------------------------------------------------*/
        case led_blink_0_state_next_pause:
        case led_blink_1_state_next_pause:
        case led_blink_2_state_next_pause:
            avalanche_gpio_set(state->map2, led->pos_map, state);  /*--- ausschalten ---*/
            blink_count--;
            if(blink_count == 0) {
                os_timer_add (state->os_timer, state->param1 * 5, (int)state);
                blink_count = state->param2;
            } else {
                if(blink_state == led_blink_2_state_next_pause)
                    os_timer_add (state->os_timer, state->param1 * 2, (int)state);
                else
                    os_timer_add (state->os_timer, state->param1, (int)state);
            }
            switch(blink_state) {
                case led_blink_0_state_next_pause:
                    set_blink_state("(LED_BLINK_CODE0: handle led_blink_all_state_next_pause )", blink_state, led_blink_0_state_next_on_0);
                    break;
                case led_blink_1_state_next_pause:
                    set_blink_state("(LED_BLINK_CODE1: handle led_blink_all_state_next_pause )", blink_state, led_blink_1_state_next_on_0);
                    break;
                case led_blink_2_state_next_pause:
                    set_blink_state("(LED_BLINK_CODE2: handle led_blink_all_state_next_pause )", blink_state, led_blink_2_state_next_on_0);
                    break;
                default:
                    break;
            }
            break;
        default:
            DEB_ERR("[led_blink_code_timer_func] unknown blink-state %u\n", blink_state);
            break;
    }
    if(state->timer_running)
        state->timer_running = (blink_state << 1) | (blink_count << 9) | 1;
    spin_unlock_irqrestore(&avm_led_lock, flags);
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static void led_on(STATE_ENTRY_T * state) {
    LED_OBJ_T *led = state->led;

    DEB_INFO("[led_on]: instance = %u pos 0x%X (State %u -> %u)\n", state->instance, led->pos_map[0], led->state[state->instance], LED_ON);
    led->state[state->instance] = LED_ON;
    avalanche_gpio_set(state->map1, led->pos_map, state);
    led->module_map |= (1 << (state->module_id));

}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static void led_off (STATE_ENTRY_T * state) {
    LED_OBJ_T *led = state->led;

    DEB_INFO("[led_off]: instance = %u pos 0x%X (State %u -> %u)\n", state->instance, led->pos_map[0], led->state[state->instance], LED_OFF);

    led->module_map &= ~(1 << (state->module_id));
    if (!led->module_map) {
        led->state[state->instance] = LED_OFF;
        avalanche_gpio_set(state->map1, led->pos_map, state);
    }
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static void led_oneshot_on (STATE_ENTRY_T * state) {
    LED_OBJ_T *led = state->led;


    if (state->timer_running) {
        DEB_WARN("[led_oneshot_on]: instance = %u pos 0x%X, cancel: timer still running\n", state->instance, led->pos_map[0]);
        return;
    }
    DEB_INFO("[led_oneshot_on]: instance = %u pos 0x%X (State %u -> %u)\n", state->instance, led->pos_map[0], led->state[state->instance], LED_ON);

    state->timer_running = 1;
    led->state[state->instance] = LED_ON;
    avalanche_gpio_set(state->map1, led->pos_map, state);
    os_timer_add (state->os_timer, state->param1,(int) state);
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static void led_oneshot_off (STATE_ENTRY_T * state) {

    LED_OBJ_T *led = state->led;

    if (state->timer_running) {
        DEB_WARN("[led_oneshot_off]: instance = %u pos 0x%X, cancel: timer still running\n", state->instance, led->pos_map[0]);
        return;
    }
    DEB_INFO("[led_oneshot_off]: instance = %u pos 0x%X (State %u -> %u)\n", state->instance, led->pos_map[0], led->state[state->instance], LED_OFF);

    state->timer_running = 1;
    led->state[state->instance] = LED_OFF;
    avalanche_gpio_set(state->map1, led->pos_map, state);
    os_timer_add (state->os_timer, state->param1,(int) state);
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static void led_flash (STATE_ENTRY_T * state) {
    LED_OBJ_T *led = state->led;

    if (state->timer_running) {
        DEB_WARN("[led_flash]: instance = %u pos 0x%X, cancel: timer still running\n", state->instance, led->pos_map[0]);
        return;
    }
    DEB_INFO("[led_flash ]: instance = %u pos 0x%X (State %u -> %u)\n", state->instance, led->pos_map[0], led->state[state->instance], LED_FLASH);

    state->timer_running = 1;
    led->state[state->instance] = LED_FLASH;
    avalanche_gpio_set(state->map1, led->pos_map, state);
    os_timer_add (state->os_timer, state->param1,(int) state);
}


/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static void led_blink_code0 (STATE_ENTRY_T * state) {
    LED_OBJ_T *led = state->led;
    enum _led_blink_state blink_state = 0;
    unsigned int blink_count;

    if (state->timer_running) {
        DEB_WARN("[led_blink_code0]: instance = %u pos 0x%X cancel: timer still running\n", state->instance, led->pos_map[0]);
        return;
    }

    DEB_INFO("[led_blink_code0]: instance = %u pos 0x%X (State %u -> %u)\n", state->instance, led->pos_map[0], led->state[state->instance], LED_BLINK_CODE0);

    set_blink_state("(led_blink_code0)", blink_state, led_blink_0_state_next_pause);
    blink_count = state->param2;
    led->state[state->instance] = LED_BLINK_CODE0;

    state->timer_running = (blink_state << 1) | (blink_count << 9) | 1;

    avalanche_gpio_set(state->map1, led->pos_map, state);
    os_timer_add (state->os_timer, state->param1,(int) state);
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static void led_blink_code1 (STATE_ENTRY_T * state) {
    LED_OBJ_T *led = state->led;
    enum _led_blink_state blink_state = 0;
    unsigned int blink_count;

    if (state->timer_running) {
        DEB_WARN("[led_blink_code1]: instance = %u pos 0x%X cancel: timer still running\n", state->instance, led->pos_map[0]);
        return;
    }

    DEB_INFO("[led_blink_code1]: instance = %u pos 0x%X (State %u -> %u)\n", state->instance, led->pos_map[0], led->state[state->instance], LED_BLINK_CODE0);

    set_blink_state("(led_blink_code1)", blink_state, led_blink_1_state_next_off_0);
    blink_count = state->param2;
    state->timer_running = (blink_state << 1) | (blink_count << 9) | 1;
    led->state[state->instance] = LED_BLINK_CODE1;

    avalanche_gpio_set(state->map1, led->pos_map, state);
    os_timer_add (state->os_timer, (state->param1) / 4 ,(int) state);
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static void led_blink_code2 (STATE_ENTRY_T * state) {
    LED_OBJ_T *led = state->led;
    enum _led_blink_state blink_state = 0;
    unsigned int blink_count;

    if (state->timer_running) {
        DEB_WARN("[led_blink_code2]: instance = %u pos 0x%X cancel: timer still running\n", state->instance, led->pos_map[0]);
        return;
    }

    DEB_INFO("[led_blink_code2]: instance = %u pos 0x%X (State %u -> %u)\n", state->instance, led->pos_map[0], led->state[state->instance], LED_BLINK_CODE0);

    set_blink_state("(led_blink_code2)", blink_state, led_blink_2_state_next_off_0);
    blink_count = state->param2;
    state->timer_running = (blink_state << 1) | (blink_count << 9) | 1;
    led->state[state->instance] = LED_BLINK_CODE2;

    avalanche_gpio_set(state->map1, led->pos_map, state);
    os_timer_add (state->os_timer, (state->param1) / 4,(int) state);
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static int led_get_mode(LED_CONFIG_T *led_cfg) {
    int num_gpio = led_cfg->gpio_num;
    int i;
    int *led_mode = led_cfg->mode;
    int max = -1;

    /* Return Max of available modes */
    for(i = 0; i < num_gpio ; i++) {
       max = (max > led_mode[i]) ? max : led_mode[i];
    }
  
    return max; 
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static void led_assign_timer(STATE_ENTRY_T *state_entry) {

     if (state_entry->os_timer) {
         os_timer_delete(state_entry->os_timer);
     }

     switch(state_entry->mode) {
         case LED_ONESHOT_ON:
              state_entry->os_timer = os_timer_init(led_oneshot_on_timer_func);
              break;

         case LED_ONESHOT_OFF:
              state_entry->os_timer = os_timer_init(led_oneshot_off_timer_func);
              break;

         case LED_FLASH:
              state_entry->os_timer = os_timer_init(led_flash_timer_func);
              break;

         case LED_BLINK_CODE0:
         case LED_BLINK_CODE1:
         case LED_BLINK_CODE2:
              state_entry->os_timer = os_timer_init(led_blink_code_timer_func);
              break;

         default:
              log_msg("invalid mode in function led_assign timer\n");
     }
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static int led_get_map(LED_CONFIG_T *led_cfg, int *p_pos_map, int *p_map1, int *p_map2) {
    int i;
    int map1[2] = {0,0};
    int pos_map[2] = {0,0};
    int map2[2] = {0,0};
    int requires_timer = REQUIRES_TIMER(led_get_mode(led_cfg));    

    for (i = 0; i < led_cfg->gpio_num; i++) {
        int gpio_map;
        int index = led_cfg->gpio[i]/32;
        int pos = led_cfg->gpio[i] % 32;
        
        if (led_cfg->gpio[i] >= num_gpios) {
            log_msg ("Error: gpio number out of range\n");
            return -1;
        }

        gpio_map = 1 << pos;

        pos_map[index] |= gpio_map;
        

        switch (led_cfg->mode[i]) {
            case LED_OFF:
                if(gpio_offstate_map[index] & gpio_map)
                    map1[index] |= gpio_map;
                
                if (requires_timer && (gpio_offstate_map[index] & gpio_map))
                    map2[index] |= gpio_map;
                break;

            case LED_ON:

                if(!(gpio_offstate_map[index] & gpio_map))
                    map1[index] |= gpio_map;
                
                if (requires_timer && !(gpio_offstate_map[index] & gpio_map))
                    map2[index] |= gpio_map;
                break;

            case LED_ONESHOT_OFF:

                if ((gpio_offstate_map[index] & gpio_map))
                    map1[index] |= gpio_map;
                else
                    map2[index] |= gpio_map;
                break;

            case LED_ONESHOT_ON:
            case LED_FLASH:
            case LED_BLINK_CODE0:
            case LED_BLINK_CODE1:
            case LED_BLINK_CODE2:

                if (!(gpio_offstate_map[index] & gpio_map))
                    map1[index] |= gpio_map;
                else
                    map2[index] |= gpio_map;
                break;

            default:
                log_msg ("Error: Invalid mode\n");
                return -1;
        }
    }

    assign_map(p_pos_map,pos_map);
    assign_map(p_map1,map1);
    assign_map(p_map2,map2);
 
    return 0;
}




/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static int configure_state(STATE_ENTRY_T *state_entry, LED_CONFIG_T *led_cfg) {
//    int state = led_cfg->state;
    int i;
    int map1[2] ;
    int pos_map[2];
    int map2[2];

    if((state_entry->mode = led_get_mode(led_cfg)) >=  NUM_LED_MODES) {
        log_msg ("Error: Invalid mode in func configure_state\n");
        return -1;
    }

    state_entry->handler = led_mode_handler[state_entry->mode];

 
    if(led_get_map(led_cfg, pos_map, map1,map2)) {
        log_msg ("Error: gpio number out of range\n");
        return -1;
    }

    assign_map(state_entry->map1,map1);
    assign_map(state_entry->map2,map2);
    state_entry->led = get_led(pos_map);

    /* Check if the state requires timer */
    if(REQUIRES_TIMER(state_entry->mode)) {
        state_entry->timer_running = 0;
        state_entry->param1 = led_cfg->param1;
        state_entry->param2 = led_cfg->param2;
        led_assign_timer(state_entry);
    }
   
    /* enable gpio pins */
    for(i = 0 ; i < led_cfg->gpio_num ; i++) {
        avm_gpio_ctrl(led_cfg->gpio[i], GPIO_PIN, GPIO_OUTPUT_PIN);
    }

    return 0;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static void free_all_states(void) {
    int module_id;

    for(module_id = 0; module_id < MAX_MODULE_ENTRIES; module_id++) {
        if(modules[module_id]) {
            int i;
            for(i = 0; i< MAX_MODULE_INSTANCES; i++) {
                MODULE_INSTANCE_T *module_instance = modules[module_id]->module_instance[i];

                if(module_instance) {
                    int state_id;

                    for(state_id =0; state_id < MAX_STATE_ENTRIES; state_id++) {
                        STATE_ENTRY_T *state= module_instance->states[state_id];

                        if(state) {
                            if(state->os_timer)
                                os_timer_delete(state->os_timer);

                            os_free(state);
                            module_instance->states[state_id] = NULL;
                        }

                    }
                }

            }
            os_free(modules[module_id]->name);
            os_free(modules[module_id]);
            modules[module_id] = NULL;
        }
    }
}

/***********************************HAL APIS************************************/
/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
void *avalanche_led_events;

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
void avalanche_led_event_notify(void *Context, enum _avm_event_id id) {
    unsigned int i, ii;
    DEB_EVENT("[avalanche_led_event_notify] Context=0x%x id=%u\n", Context, id);

    if(id != avm_event_id_led_status) return;
    if(avalanche_led_events == NULL) return;
    for(i =  0 ; i < MAX_MODULE_ENTRIES ; i++) {
        void *ptr;
        if(modules[i] == NULL)
            continue;
        DEB_EVENT("[avalanche_led_event_notify] module %u found\n", i);
        for(ii = 0 ; ii < MAX_MODULE_INSTANCES ; ii++) {
            if(modules[i]->module_instance[ii] == NULL)
                continue;
            if(modules[i]->module_instance[ii]->event.buffer == NULL)
                continue;
            DEB_EVENT("[avalanche_led_event_notify] module %u instance %u found\n", i, ii);
            ptr = kmalloc(modules[i]->module_instance[ii]->event.buffer_len, GFP_ATOMIC);
            if(ptr == NULL)
                continue;
            memcpy(ptr, 
                    modules[i]->module_instance[ii]->event.buffer, 
                    modules[i]->module_instance[ii]->event.buffer_len);
            avm_event_source_trigger(avalanche_led_events, avm_event_id_led_status, 
                modules[i]->module_instance[ii]->event.buffer_len, ptr);
            DEB_EVENT("[avalanche_led_event_notify] event sent\n");
        }
    }
}


/**************************************************************************
 * FUNCTION NAME : avalanche_led_hal_init
 **************************************************************************
 * DESCRIPTION   :
 *      The function initializes led hal module
 *
 * RETURNS               :
 *      0               on Success
 *      Negative value  on Error
 ***************************************************************************/
int avalanche_led_hal_init (int  *gpio_off_value, int num_gpio_pins) {
    //int i;

    num_gpios = num_gpio_pins + 4;
    assign_map(gpio_offstate_map, gpio_off_value);

    spin_lock_init(&avm_led_lock);

    /* initialize led state function handlers */
    led_mode_handler[LED_ON]          = led_on;
    led_mode_handler[LED_OFF]         = led_off;
    led_mode_handler[LED_ONESHOT_ON]  = led_oneshot_on;
    led_mode_handler[LED_ONESHOT_OFF] = led_oneshot_off;
    led_mode_handler[LED_FLASH]       = led_flash;
    led_mode_handler[LED_BLINK_CODE0] = led_blink_code0;
    led_mode_handler[LED_BLINK_CODE1] = led_blink_code1;
    led_mode_handler[LED_BLINK_CODE2] = led_blink_code2;

    memset(&led_state, 0, sizeof(led_state));

    avalanche_led_events = avm_event_source_register("led-events", (1ULL << avm_event_id_led_status), avalanche_led_event_notify, NULL);
    DEB_EVENT("[avalanche_led_hal_init] avalanche_led_events = 0x%x\n", avalanche_led_events);
    return 0;
}


/**************************************************************************
 * FUNCTION NAME : avalanche_led_config_set
 **************************************************************************
 * DESCRIPTION   :
 *      The function configures LED state object
 *
 * RETURNS               :
 *      0               on Success
 *      Negative value  on Error
 ***************************************************************************/
int avalanche_led_config_set(LED_CONFIG_T * led_cfg) {
    MODULE_INSTANCE_T *module;
    unsigned long flags;
    spin_lock_irqsave(&avm_led_lock, flags);
    module = get_module (led_cfg->name, led_cfg->instance);

    if (!module)
        goto config_failed;

    module->event.id = led_cfg->event_id;
    DEB_EVENT("[avalanche_led_config_set] id = %u state = %u event_name = %s\n", 
            led_cfg->event_id,  led_cfg->state, led_cfg->event_name);

    if (led_cfg->state < MAX_STATE_ENTRIES) {
        int state = led_cfg->state;

        if (!(module->states[state])) {
            module->states[state] = led_malloc (sizeof (STATE_ENTRY_T));
        }

        if (!(module->states[state]))
            goto config_failed;

        module->states[state]->module_id = module->module_id;	
        module->states[state]->instance  = led_cfg->instance;

        if(configure_state(module->states[state], led_cfg)) {
            os_free(module->states[state]);
            module->states[state] = NULL;
            goto config_failed;
        }

    } else {
        log_msg ("ERROR:State Count exceeded\n");
        goto config_failed;
    }

    spin_unlock_irqrestore(&avm_led_lock, flags);

    return 0;

config_failed:

    spin_unlock_irqrestore(&avm_led_lock, flags);
    return -1;
}

/**************************************************************************
 * FUNCTION NAME : avalanche_led_register
 **************************************************************************
 * DESCRIPTION   :
 *      The function creates handle to the given module and returns it.
 *
 * RETURNS               :
 *      Handle to the module instance on success.
 *      NULL  on failure.
 ***************************************************************************/

void *avalanche_led_register (const char *mod_name, int instance) {
    void *p;
    unsigned long flags;
    spin_lock_irqsave(&avm_led_lock, flags);
    p = (void *)get_module ((char *)mod_name, instance);
    spin_unlock_irqrestore(&avm_led_lock, flags);
    return p;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
void avalanche_led_late_actions(void) {
    int i;
    avalanche_led_configurated = 1;
    for(i = 0 ; i < MAX_LATE_ACTIONS ; i++) {
        if(avalanche_led_late_action[i].handle) {
            DEB_INFO("[avalanche_led_late_actions]: set state %u for handle 0x%x\n", 
                avalanche_led_late_action[i].state, (unsigned int)avalanche_led_late_action[i].handle);
            avalanche_led_action(avalanche_led_late_action[i].handle, avalanche_led_late_action[i].state | 0x8000);
            avalanche_led_late_action[i].handle = NULL;
        }
    }
    avalanche_led_configurated = 2;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
void avalanche_led_event_action (void *module, int state_id, unsigned int param_len, void *user_ptr) {
    DEB_EVENT("[avalanche_led_event_action] module = 0x%x new state %u param_len %u user_ptr 0x%x\n",
            module, state_id, param_len, user_ptr);
    if(avalanche_led_events) {
        struct _avm_event_led_status *event;
        event = kmalloc(sizeof(struct _avm_event_led_status) + param_len, GFP_ATOMIC);
        if(event) {
            struct _avm_event_len_status *E;
            MODULE_INSTANCE_T *M = (MODULE_INSTANCE_T *)(module);
            event->header.id = avm_event_id_led_status;
            event->led       = M->event.id;
            event->state     = state_id;
            event->param_len = param_len;
            if(param_len)
                copy_from_user(event->params, user_ptr, param_len);
            E = kmalloc(sizeof(struct _avm_event_led_status) + param_len, GFP_ATOMIC);
            if(E) {
                memcpy(E, event, sizeof(struct _avm_event_led_status) + param_len);
                if(M->event.buffer)
                    kfree(M->event.buffer);
                M->event.buffer_len = sizeof(struct _avm_event_led_status) + param_len;
                M->event.buffer = E;
            }
            DEB_EVENT("[avalanche_led_event_action] send id = %u event id = %u state = %u param_len = %u\n",
                    event->header.id, event->led, event->state, event->param_len);
            avm_event_source_trigger(avalanche_led_events, avm_event_id_led_status, sizeof(struct _avm_event_led_status) + param_len, event);
        }
    } else {
        DEB_EVENT("[avalanche_led_event_action] event quelle not registered\n");
    }
    avalanche_led_action (module, state_id);
}

/**************************************************************************
 * FUNCTION NAME : avalanche_led_action
 **************************************************************************
 * DESCRIPTION   :
 *      The function triggers action on LED
 *
 ***************************************************************************/
void avalanche_led_action (void *module, int state_id) {

    unsigned long flags;
    spin_lock_irqsave(&avm_led_lock, flags);
    if(avalanche_led_configurated == 0) {
        int i;
        if(module == NULL) {
            DEB_ERR("[avalanche_led_action] no handle\n");
            spin_unlock_irqrestore(&avm_led_lock, flags);
            return;
        }
        for(i = 0 ; i < MAX_LATE_ACTIONS ; i++) {
            if(avalanche_led_late_action[i].handle == module) {
                avalanche_led_late_action[i].state = state_id;
                DEB_INFO("[avalanche_led_action]: save state %u for known handle 0x%p\n", 
                    avalanche_led_late_action[i].state, avalanche_led_late_action[i].handle);
                break;
            }
            if(avalanche_led_late_action[i].handle == NULL) {
                avalanche_led_late_action[i].handle = module;
                avalanche_led_late_action[i].state  = state_id;
                DEB_INFO("[avalanche_led_action]: save state %u for new handle 0x%p\n", 
                    avalanche_led_late_action[i].state, avalanche_led_late_action[i].handle);
                break;
            }
        }
        spin_unlock_irqrestore(&avm_led_lock, flags);
        return;
    }

    if(avalanche_led_configurated == 1) {
        if(state_id & 0x8000) {
            state_id &= ~0x8000;
        } else {
            spin_unlock_irqrestore(&avm_led_lock, flags);
            return;
        }
    }

    if (module && (state_id < MAX_STATE_ENTRIES)) {

        STATE_ENTRY_T *state = ((MODULE_INSTANCE_T *) (module))->states[state_id];
        if (state) {
            state->handler (state);
        } else {
#if 0
            int id = ((MODULE_INSTANCE_T *) (module))->module_id;
            char *name = modules[id]->name;
            name = name ? name : "unknown";
            DEB_WARN("[avalanche_led_action]: '%s' state_id %u not valid\n", name, state_id);
#endif
        }
    } else {
        DEB_ERR("[avalanche_led_action]: ERROR: module = 0x%p (null) or state_id(%u) >= MAX(%u)\n", module, state_id, MAX_STATE_ENTRIES);
    }
    spin_unlock_irqrestore(&avm_led_lock, flags);
    return;
}


/**************************************************************************
 * FUNCTION NAME : avalanche_led_unregister
 **************************************************************************
 * DESCRIPTION   :
 *      The function unregisters the module
 *
 * RETURNS               :
 *      0               on Success
 *      Negative value  on Error
 ***************************************************************************/

int avalanche_led_unregister (void *mod_inst) {
    return 0;
}


/**************************************************************************
 * FUNCTION NAME : led_free_all
 **************************************************************************
 * DESCRIPTION   :
 *      The function frees the memory allocated for holding state
 *       configuration data
 *
 ***************************************************************************/

void avalanche_led_free_all(void) {
    free_all_states();
}

/**************************************************************************
 * FUNCTION NAME : avalanche_led_hal_exit
 **************************************************************************
 * DESCRIPTION   :
 *      The function releases all the allocated memory
 *
 ***************************************************************************/

void avalanche_led_hal_exit(void) {
    free_all_states();
}

/*****************************************************************************
 * FUNCTION NAME : avalanche_led_config_get
 *****************************************************************************
 * DESCRIPTION   :
 *      The function returns configuration information corresponding to module
 *      state.
 *
 * RETURNS               :
 *      0               on Success
 *      Negative value  on Error
 ***************************************************************************/
int avalanche_led_config_get(LED_CONFIG_T *led_cfg,int module_id,int instance, int state_id) {
    if(module_id == -1) {
        /* The module info is passed through  field of led_cfg */
        MODULE_INSTANCE_T *mod = get_module (led_cfg->name, instance);
        if(mod)
            module_id = mod->module_id;

    }
    if(module_id >= MAX_MODULE_ENTRIES ||  module_id < 0)
        return -1;

    if(state_id >= MAX_STATE_ENTRIES ||  module_id < 0)
        return -1;

    if(instance >= MAX_MODULE_INSTANCES ||  module_id < 0)
        return -1;

    if(modules[module_id]) {
         MODULE_INSTANCE_T *module = modules[module_id]->module_instance[instance];
         if(module) {
             STATE_ENTRY_T *state = module->states[state_id];
             if(state) {
                 int i;
                 LED_OBJ_T *led;
                 strcpy(led_cfg->name, modules[module_id]->name);
                 led_cfg->state = state_id;
                 led_cfg->instance = instance;
                 led_cfg->param1 = state->param1;
                 led_cfg->param2 = state->param2;
                 led_cfg->mode[0] = state->mode;
                 led = state->led;
                 
                 /* needs to be modified for multi-pin leds */
                 for(i = 0;i < num_gpios && !(led->pos_map[i/32] & (1 << i)); i++);

                 led_cfg->gpio[0] = i;
                 led_cfg->gpio_num = 1;

                 return 0;
             }
         }
    }

    return -1;
}


/*******************Export Symbols********************************************/
EXPORT_SYMBOL(avalanche_led_action);
EXPORT_SYMBOL(avalanche_led_register);
EXPORT_SYMBOL(avalanche_led_unregister);
/*****************************************************************************/
