/*------------------------------------------------------------------------------------------*\
 *   
 *   Copyright (C) 2006 AVM GmbH <fritzbox_info@avm.de>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
\*------------------------------------------------------------------------------------------*/

/*------------------------------------------------------------------------------------------*\
 * In dieser Datei werden die moeglichen Blinkmodi definiert bzw. erzeugt.                  *
\*------------------------------------------------------------------------------------------*/

#include <linux/config.h>
#include <linux/version.h>
#include <linux/timer.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/avm_led.h>
#include <linux/avm_profile.h>
#include <linux/kallsyms.h>
#include <asm/uaccess.h>
#include "avm_sammel.h"
#include "avm_led.h"

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
#include <asm/mach_avm.h>
#else
#endif

/*------------------------------------------------------------------------------------------*\
 * avm_led_profile.c
\*------------------------------------------------------------------------------------------*/
int avm_led_profile_init(unsigned int table_index, struct _avm_virt_led *V) {
    switch(table_index) {
        case 0:  /* off */
            printk("[avm_led] profile_init: disable\n");
            return 1;
        case 1: /* on */
            printk("[avm_led] profile_init: enable\n");
            return 2;
        default:
            printk("[avm_led] profile_init: other %u\n", table_index);
            return 33;
    }
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
void avm_led_profile_exit(int handle) {
    return;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
void avm_led_profile_stop(int handle) {
    return;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
void avm_led_profile_timer(unsigned long handle) {
    return;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
void avm_led_profile_action(int handle, unsigned int param1, unsigned int param2) {
    extern struct _avm_profile_data *ohio_simple_profiling_enable(unsigned int on, unsigned int *count);
    unsigned int len;
    struct _avm_profile_data *data;
    static unsigned char Buffer[512], Symbols[256];
    static struct file *fp;
    mm_segment_t  oldfs;  
    size_t bytesWritten;

    if(handle == 33)
        return;

    printk("[avm_led] profile_action: %s\n", handle ? "enable" : "disable");

    if(handle == 1) {
        fp = filp_open("/var/profile.log", O_WRONLY | O_CREAT, 0666);
        if(IS_ERR(fp)) {
            printk(KERN_ERR "[avm_led] Failed: Could not open \"/var/profile.log\"\n");
            return;
        }
        /* Lesezugriff auf File(system) erlaubt? */
        if (fp->f_op->write == NULL) {
            printk(KERN_ERR "[avm_led] Failed: Could not write \"/var/profile.log\"\n");
            return;
        }

        /* Von Anfang an lesen */
        fp->f_pos = 0;
    }

    data = ohio_simple_profiling_enable(handle - 1, &len);
    if(data && len) {
        int i;
        printk("[avm_led_profile_action] %u entries logged\n", len);


        for(i = 0 ; i < len ; i++) {
            unsigned int _addr;
            unsigned int len;
            char *_text;
            sprintf(Buffer, "0x%08X: ", data[i].time);
            switch(data[i].addr & 0xE0000000) {
                case KERNEL_PERFOMANCE_TRACE_SKB:
                    _addr =  (data[i].addr & ~0xE0000000) | 0x80000000;
                    sprintf(Buffer + strlen(Buffer), " (skb log 0x%x) addr=0x%08x\n", (data[i].addr >> (29 - 6)) & ((1 << 6) - 1), _addr);
                    break;

                case KERNEL_PERFOMANCE_TRACE_SHARE:
                    _addr = data[i].addr;
                    sprintf(Buffer + strlen(Buffer), "0x%08x (shared lib)", _addr);
                    break;
                case KERNEL_PERFOMANCE_USR_TRACE:
                    _addr = data[i].addr;
                    sprintf(Buffer + strlen(Buffer), "0x%08x (user appl context)", _addr);
                    break;

                case KERNEL_PERFOMANCE_TRACE_VM:
                case KERNEL_PERFOMANCE_TRACE_KERNEL:
                    _addr = data[i].addr;
                    __sprint_symbol(Symbols, _addr);
                    sprintf(Buffer + strlen(Buffer), "0x%08x %s", _addr, Symbols);
                    break;
                case KERNEL_PERFOMANCE_TRACE_RESERVED1:
                case KERNEL_PERFOMANCE_TRACE_RESERVED2:
                case KERNEL_PERFOMANCE_TRACE_RESERVED3:
                    break;
            }
            strcat(Buffer, "\n");
            len = strlen(Buffer);
            oldfs = get_fs();
            set_fs(KERNEL_DS);
            bytesWritten = fp->f_op->write(fp, Buffer, len, &fp->f_pos);
            if(bytesWritten != len) {
                printk(KERN_ERR "memory full (abort writing file)\n");
                set_fs(oldfs);
                break;
            }
            set_fs(oldfs);
            /*--- printk("%s", Buffer); ---*/
        }
    }
    if(handle == 1) {
        filp_close(fp, NULL);
    }
    return;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
void avm_led_profile_sync(int virt_led_handle) {
    return;
}

