/******************************************************************************    
 * FILE PURPOSE:     - LED driver module Source                                       
 ******************************************************************************    
 * FILE NAME:     led_drv.c                                                        
 *                                                                                 
 * DESCRIPTION:   Linux LED character driver implementation                                                  
 *                                                                                 
 * 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 <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/delay.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/proc_fs.h>

#include <asm/mach_avm.h>
#include <asm/mach-ohio/led_ioctl.h>
#include <asm/mach-ohio/led_config.h>
#include "led_hal.h"
#include "led_platform.h"

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
MODULE_LICENSE("\n(C) Copyright 2003, Texas Instruments, Inc\n(C) Copyright 2004, 2005, AVM");

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
#if 0
#define DEB_ERR(args...)     printk(KERN_ERR args)
#define DEB_WARN(args...)    printk(KERN_WARNING args)
#define DEB_INFO(args...)    printk(KERN_INFO args)
#define DEB_TRACE(args...)   printk(KERN_INFO args)
#else
#define DEB_ERR(args...)     printk(KERN_ERR args)
#define DEB_WARN(args...)
#define DEB_INFO(args...)
#define DEB_TRACE(args...)
#endif

extern void hardware_error_init(void);

/*------------------------------------------------------------------------------------------*\
 * FIXME Diesen Bereich unbedingt ansehen, ob er noch gueltig ist! FIXME                    *
\*------------------------------------------------------------------------------------------*/
#define  AVALANCHE_GPIO_OFF_MAP           {0xF34FFFC0} 
#define  AVALANCHE_GPIO_PIN_COUNT         32             /* FIXME hier evtl. 40 ? */

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
#define TI_LED_VERSION          "1.0"
#define GPIO_MAP_LEN   ((MAX_GPIO_PIN_NUM/32) + 1)

static int gpio_off_state[GPIO_MAP_LEN] = AVALANCHE_GPIO_OFF_MAP;
static struct proc_dir_entry *led_dir;

struct _avm_led {
	struct char_device_struct *device_region;
    dev_t                      device;
    struct cdev               *cdev;
} avm_led;

static int led_ioctl( struct inode * inode, struct file * file,
                             unsigned int cmd, unsigned long arg )
{

    int ret = 0;
//    char name[80];

    DEB_TRACE("[led_ioctl]: cmd = %u\n", cmd);

    switch ( cmd )
    {
        case LED_CONFIG:
            {
                LED_CONFIG_T led_cfg;
                if (copy_from_user((char *)&led_cfg, (char *)arg, sizeof(led_cfg)))
                {
                    ret = -EFAULT;
                    break;
                }
                DEB_TRACE("[led_ioctl/LED_CONFIG]: led_cfg: name=%s instance=%u state=%u gpio_num=%u param1=%u param2=%u\n",
                    led_cfg.name, led_cfg.instance, led_cfg.state,
                    /*--- unsigned int gpio[MAX_GPIOS_PER_STATE]; ---*/
                    /*--- unsigned int mode[MAX_GPIOS_PER_STATE]; ---*/
                    led_cfg.gpio_num, led_cfg.param1, led_cfg.param2);
                        
                ret = avalanche_led_config_set(&led_cfg);
            }
            break;

        case LED_CONFIG_DONE:
            DEB_TRACE("[led_ioctl/LED_CONFIG_DONE]: \n");
            avalanche_led_late_actions();
            ret = 0;
            break;


        case LED_GET_HANDLE:
            {
                LED_MODULE_T led_mod;
                int handle;

                if (copy_from_user((char *)&led_mod, (char *)arg, sizeof(led_mod)))
                {
                    ret =  -EFAULT;
                    break;
                }

                DEB_TRACE("[led_ioctl/LED_GET_HANDLE]: led_mod: name=%s instance=%u\n", led_mod.name, led_mod.instance);

                handle = (int)avalanche_led_register(led_mod.name, led_mod.instance);

                if (copy_to_user((char *)(&(((LED_MODULE_T *)arg)->handle)), (char *)(&handle), sizeof(int)))
                {
                    ret =  -EFAULT;
                    break;
                }

                DEB_TRACE("[led_ioctl/LED_GET_HANDLE]: led_mod: handle=0x%x\n", handle);

                if(handle)
                    ret = 0;
                else
                    ret = -1;
            }
            break;

        case LED_ACTION:
            {
                LED_STATE_T led_state;

                if (copy_from_user((char *)&led_state, (char *)arg, sizeof(led_state)))
                {
                    ret =  -EFAULT;
                    break;
                }

                DEB_TRACE("[led_ioctl/LED_ACTION]: led_state: handle=0x%x state=%u\n", led_state.handle, led_state.state_id);

                avalanche_led_action((void *)led_state.handle, led_state.state_id);
            
                            
            }
            break;

        case LED_RELEASE_HANDLE:
            DEB_TRACE("[led_ioctl/LED_RELEASE_HANDLE]: handle=0x%x\n", arg);
            ret = avalanche_led_unregister((void *)arg);
        break;

        default:
            ret = -EINVAL;
    }
    return ret;

}

static int led_open( struct inode * inode, struct file * file )
{
    return 0;
}

static int led_close( struct inode * inode, struct file * file )
{
    return 0;
}

struct file_operations led_fops = {
    ioctl:    led_ioctl,
    open:    led_open,
    release:  led_close
};


/* Proc function to display driver version */                                                                       
static int                                                                                     
led_ver_info(char *buf, char **start, off_t offset, int count, int *eof, void *data)        
{                                                                                              
//	int instance;                                                                              
	int len=0;                                                                                 
                                                                                               
	len += sprintf(buf +len,"TI Linux LED Driver Version %s\n", TI_LED_VERSION);         
	return len;                                                                                
}                                                                                              


/* proc interface /proc/led/led_cfg  */
int led_cfg_info(char* buf, char **start, off_t offset, int count, int *eof, void *data) {

    int mod_count = 0;
    int i;
    int len=0;
    int limit = count - 80;
    char *msg[NUM_LED_MODES] = { "LED OFF", "LED_ON", "LED_ONESHOT_OFF", "LED_ONESHOT_ON", "LED_FLASH", "LED_BLINK_CODE0", "LED_BLINK_CODE1", "LED_BLINK_CODE2" };

    for(mod_count = 0;mod_count<MAX_MODULE_ENTRIES;mod_count++)
    {
        int instance;

        for(instance = 0; instance < MAX_MODULE_INSTANCES; instance++)
        {  
            int state_id;
            int flag = 0;
          
            for(state_id =0 ;state_id < MAX_STATE_ENTRIES; state_id++)
            {
                LED_CONFIG_T led_cfg;
                if(avalanche_led_config_get(&led_cfg, mod_count, instance, state_id) == 0)
                { 
                    if(flag == 0)
                    {
                        flag = 1;
                        if(len <= limit)
                            len+= sprintf(buf+len, "Module:%s\n",led_cfg.name);
                        
                        if(len <= limit)
                            len+= sprintf(buf+len, "    Instance:%d\n",instance);
                        
                    }
                    
                    if(len <= limit)
                        len+= sprintf(buf+len, "        State: %d\n",state_id);
                    
                    if(led_cfg.gpio_num > 1) {
                        if(len <= limit)
                            len += sprintf(buf + len, "            number of gpios: %u\n", led_cfg.gpio_num);
                    }

                    for(i = 0 ; i < led_cfg.gpio_num ; i++) {
                        if(len <= limit)
                            len+= sprintf(buf+len, "            mode: %s\n", msg[led_cfg.mode[0] % NUM_LED_MODES]);
                        
                        if(len <= limit)
                            len+= sprintf(buf+len, "            gpio: %d\n",led_cfg.gpio[0]);
                    }
                    
                    if(len <= limit) {
                        char *text = "param1";
                        switch(led_cfg.mode[0]) {
                            case LED_ONESHOT_OFF:
                                text = "off-time [ms]"; break;
                            case LED_ONESHOT_ON:
                            case LED_FLASH:
                            case LED_BLINK_CODE0:
                            case LED_BLINK_CODE1:
                            case LED_BLINK_CODE2:
                                text = "on-time [ms]"; break;
                        }
                        len+= sprintf(buf+len, "            %s: %d\n", text, led_cfg.param1);
                    }
                    
                    if(len <= limit) {
                        char *text = "param2";
                        switch(led_cfg.mode[0]) {
                            case LED_FLASH:
                                text = "off-time [ms]"; break;
                            case LED_BLINK_CODE0:
                            case LED_BLINK_CODE1:
                            case LED_BLINK_CODE2:
                                text = "blink code"; break;
                        }
                        len+= sprintf(buf+len, "            %s: %d\n", text, led_cfg.param2);
                    }
                }
            }
        }
    }       

    return len;
}    

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
int led_init(void) {
    int reason;

    /* register LED device */
    DEB_INFO("[avm_led] alloc_chrdev_region()\n");
    avm_led.device = MKDEV(AVM_LED_MAJOR, 0);
    reason = register_chrdev_region(avm_led.device, 1, "led");
    if(reason) {
        DEB_ERR("[avm_led] register_chrdev_region failed: reason %d!\n", reason);
        return -ERESTARTSYS;
    }

	avm_led.cdev = cdev_alloc();
	if (!avm_led.cdev) {
        unregister_chrdev_region(avm_led.device, 1);
        DEB_ERR("[avm_led] cdev_alloc failed!\n");
        return -ERESTARTSYS;
    }

	avm_led.cdev->owner = led_fops.owner;
	avm_led.cdev->ops = &led_fops;
	kobject_set_name(&(avm_led.cdev->kobj), "led");

    if (cdev_add(avm_led.cdev, avm_led.device, 1)) { 
        kobject_put(&avm_led.cdev->kobj);
        unregister_chrdev_region(avm_led.device, 1);
        printk("[avm_led] cdev_add failed!\n");
        return -ERESTARTSYS;
    }

    avalanche_led_hal_init(gpio_off_state,AVALANCHE_GPIO_PIN_COUNT);

    /* create proc entry */
#ifdef CONFIG_PROC_FS
    led_dir = proc_mkdir("led", NULL);
	if (led_dir) {
        create_proc_read_entry("led_cfg", 0666, led_dir, led_cfg_info, NULL);
        create_proc_read_entry("led_ver", 0, led_dir, led_ver_info, NULL);
    }
#endif
    hardware_error_init();

    return 0;
}

void led_exit(void) {
    avalanche_led_hal_exit();
    if(avm_led.cdev) {
        cdev_del(avm_led.cdev); /* Delete char device */
        unregister_chrdev_region(avm_led.device, 1);
    }
    return;
}

