/*------------------------------------------------------------------------------------------*\
 *   
 *   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 
\*------------------------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------------------------*\
\*-----------------------------------------------------------------------------------------------*/
#include <linux/config.h>
#include <linux/version.h>
#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/io.h>
#include <linux/file.h>
#include <linux/avm_power.h>
/*--- #include <asm/semaphore.h> ---*/
#include <asm/errno.h>
/*--- #include <linux/wait.h> ---*/
/*--- #include <linux/vmalloc.h> ---*/
/*--- #include <linux/poll.h> ---*/
#include "avm_power.h"
#include "wyatt_earp.h"

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
#include <linux/cdev.h>
#include <asm/mach_avm.h>
#define LOCAL_MAJOR     AVM_POWER_MAJOR
#else
#include <linux/devfs_fs_kernel.h>
#include <asm/smplock.h>
#define LOCAL_MAJOR     0
static devfs_handle_t avm_power_devfs_handle;
#endif

/*--- FPGA-Frequenzumsetzung wird über GPIO-MISC-Register im FPGA gemappt ---*/
#define FPGA_BASE                     0xBE000000
#define GLOBAL_MISC                         0x2C
#define GLOBAL_MISC_ADDR              (volatile unsigned int *)(FPGA_BASE + GLOBAL_MISC)
#define GLOBAL_MISC_CLOCKSWITCH             0x2

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
#define MODULE_NAME     "avm_power"
MODULE_DESCRIPTION("AVM powermanagment module");
MODULE_LICENSE("GPL");

/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
#define SKIP_SPACES(p) while((p) && *(p) && ((*(p) == ' ') || (*(p) == '\t'))) (p)++;
#define CMD_MODE    "MODE"
#define WE_CMD_MODE  "WE_MODE"
#define CPU_SPEED  "CPU_SPEED"

static volatile struct _power_managment_clients *PwClientAnker;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
static spinlock_t avm_power_lock = SPIN_LOCK_UNLOCKED;
#endif/*--- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) ---*/

static unsigned int WyattEarpMode = 0xFFFF;

/*-------------------------------------------------------------------------------------*\
 * Liste von registrierten Treibern, die den PowermanagmentCallback-Befehl bekommen
\*-------------------------------------------------------------------------------------*/
static struct _power_managment_dest_entry dslmode_entries[] =  {
    { "speedup", 0, 0 },  
    { "adsl", 1, 10 },  /*--- DSL an ---*/
    { NULL, 0, 0 }
};

/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
static struct _power_managment_dest_entry atamode_entries[] =  {
    { "speedup", 0, 1 },  
    { "adsl", 1, 1 },   /*--- DSL komplett aus ---*/
    { NULL, 0, 0 }
};

/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
static struct _power_managment_dest_entry vdslmode_entries[] =  {
    { "adsl", 1, 3 },  /*--- DSL aus aber Spannung an ---*/
    { NULL, 0, 0 }
};

/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
static struct _power_managment_dest_entry updatemode_entries[] =  {
    { "speedup", 0, 0 },  
    { NULL, 0, 0 }
};

/*-------------------------------------------------------------------------------------*\
 * powermode mit nachfolgender Powermanagment-Treiber-Liste
\*-------------------------------------------------------------------------------------*/
static struct _power_managment power[] = {
    { "dsl", dslmode_entries },
    { "ata", atamode_entries },
    { "vdsl", vdslmode_entries },        /*--- im VDSL-Mode DSL auf ATA stellen ---*/
    { "update", updatemode_entries },        
    { NULL, NULL}
};


/*-----------------------------------------------------------------------------------------------*\
\*-----------------------------------------------------------------------------------------------*/
static int avm_power_open(struct inode *, struct file *);
static int avm_power_close(struct inode *, struct file *);
ssize_t avm_power_write(struct file *filp, const char *write_buffer, size_t write_length, loff_t *write_pos);
struct _power_managment_dest_entry *find_powermode(char *powermode);
void avm_power_writeformaterror(char *text);
struct _power_managment_clients *find_pwclient_by_name(char *client_name);
int powermode_action(struct _power_managment_dest_entry *powermodetab);

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
struct _avm_power avm_power;

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
struct file_operations avm_power_fops = {
    owner:   THIS_MODULE,
    open:    avm_power_open,
    release: avm_power_close,
    /*--- read:    avm_power_read, ---*/
    write:   avm_power_write,
    /*--- ioctl:   avm_power_ioctl, ---*/
    /*--- fasync:  avm_power_fasync, ---*/
    /*--- poll: avm_power_poll, ---*/
};

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static __inline int LOCK(void) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
    unsigned long flags;
    spin_lock_irqsave(&avm_power_lock, flags);
#else/*--- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) ---*/
    int flags;

	save_flags(flags);
	cli();
#endif/*--- #else ---*//*--- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) ---*/
    return flags;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static __inline void UNLOCK(int flags) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
	spin_unlock_irqrestore(&avm_power_lock, flags);
#else/*--- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) ---*/
    restore_flags(flags);
#endif/*--- #else ---*//*--- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) ---*/
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static __inline int avm_power_write_check_special_char(char **p, char check_char) {
    SKIP_SPACES(*p); /* ggf. spaces */
    if(**p != check_char) 
        return 1;
    (*p)++;
    return 0;
}
#if 0
/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
int avm_power_load_config(void) {
    static struct file *filp;
    unsigned char *buffer;
    unsigned int configLength = 0, bytesRead;
    mm_segment_t oldfs;  
    char *p;
    char filename[] = "/etc/power.conf";

    buffer = kmalloc(4096, GFP_KERNEL);
    if(buffer == NULL) {
        DEB_ERR("[avm_power] avm_power_load_config: Could not allocate memory for config file!\n");
        return -1;
    }
  
    filp = filp_open(filename, 00, O_RDONLY);
    if(IS_ERR(filp)) {
        DEB_ERR("[avm_power_load_config] Could not open config file\n");
        return -EACCES;
    }
  
    if(filp->f_dentry != NULL) {
        if(filp->f_dentry->d_inode != NULL) {
            configLength = (unsigned int)filp->f_dentry->d_inode->i_size + 0x200;
        }
    }
  
    if (filp->f_op->read == NULL) {
        return -EACCES; /* File(system) doesn't allow reads */
    }
         
    /* Disable parameter checking */
    oldfs = get_fs();
    set_fs(KERNEL_DS);
  
    /* Now read bytes from postion "StartPos" */
    filp->f_pos = 0;
  
    bytesRead = filp->f_op->read(filp, buffer, configLength, &filp->f_pos);
  
    DEB_INFO("[avm_power_load_config] file length = %d\n", bytesRead);
  
    set_fs(oldfs);
    /* Close the file */
    fput(filp);

    p = buffer;
    while(bytesRead) {
        ssize_t written;

        written = avm_power_write(NULL, (const char *) p, bytesRead, (loff_t *) NULL);
        if(written > 0) {
            p += written;
            bytesRead -= written;
        } else {
            DEB_ERR("[avm_power_load_config] Syntax error in power config file %s at \"%.20s\"\n",
                    filename, p);
            break;
        }
    }
    
    kfree(buffer);

    return 0;
}
/*--- late_initcall(avm_power_load_config); ---*/
#endif

/*-----------------------------------------------------------------------------------------------*\
\*-----------------------------------------------------------------------------------------------*/
int __init avm_power_init(void) {
#   if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
    int reason;
#   endif /*--- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) ---*/

    DEB_INFO("[avm_power] register_chrdev_region()\n");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
    spin_lock_init(&avm_power_lock);
    avm_power.device = MKDEV(LOCAL_MAJOR, 0);
    reason = register_chrdev_region(avm_power.device, 1, MODULE_NAME);
    if(reason) {
        DEB_ERR("[avm_power] register_chrdev_region failed: reason %d!\n", reason);
        return -ERESTARTSYS;
    }
	avm_power.cdev = cdev_alloc();
	if (!avm_power.cdev) {
        unregister_chrdev_region(avm_power.device, 1);
        DEB_ERR("[avm_power] cdev_alloc failed!\n");
        return -ERESTARTSYS;
    }
	avm_power.cdev->owner = avm_power_fops.owner;
	avm_power.cdev->ops = &avm_power_fops;
	kobject_set_name(&(avm_power.cdev->kobj), MODULE_NAME);
#else
    avm_power.device = register_chrdev( LOCAL_MAJOR/*--- dynamic major ---*/, MODULE_NAME, &avm_power_fops);
    if(avm_power.device < 1) {
        DEB_ERR("[%s]: register_chrdrv failed: reason %d\n", MODULE_NAME, avm_power.device);
        MOD_DEC_USE_COUNT;
        return -ERESTARTSYS;
    }
#endif
		
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
	if(cdev_add(avm_power.cdev, avm_power.device, 1)) {
        kobject_put(&avm_power.cdev->kobj);
        unregister_chrdev_region(avm_power.device, 1);
        DEB_ERR("[avm_power] cdev_add failed!\n");
        return -ERESTARTSYS;
    }
#else
    avm_power_devfs_handle = devfs_register(NULL, MODULE_NAME,
                                        DEVFS_FL_DEFAULT, avm_power.device, 0,
                                	    S_IFCHR | S_IRUGO | S_IWUSR, &avm_power_fops, NULL);
    if(avm_power_devfs_handle == NULL) {
        DEB_ERR("%s: avm_power_file: devfs_register(%s, %u ...) failed \n", MODULE_NAME, MODULE_NAME, 0);
        MOD_DEC_USE_COUNT;
        return -ENOMEM;
    }
#endif
    DEB_INFO("[avm_power]: major %d (success)\n", MAJOR(avm_power.device));
    return 0;
}
module_init(avm_power_init);

/*-----------------------------------------------------------------------------------------------*\
\*-----------------------------------------------------------------------------------------------*/
#ifdef CONFIG_AVM_POWER_MODULE
void __exit avm_power_cleanup(void) {
    DEB_INFO("[avm_power]: unregister_chrdev(%u)\n", MAJOR(avm_power.device));
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
    cdev_del(avm_power.cdev); /* Delete char device */
    unregister_chrdev_region(avm_power.device, 1);
#else
    devfs_unregister(avm_power_devfs_handle);
    devfs_unregister_chrdev(avm_power.device, "avm_power");
#endif
    return;
}
module_exit(avm_power_cleanup);
#endif /*--- #ifdef CONFIG_AVM_LED_MODULE ---*/

/*-----------------------------------------------------------------------------------------------*\
\*-----------------------------------------------------------------------------------------------*/
static int avm_power_open(struct inode *inode, struct file *filp) {
    struct _avm_power_open_data *open_data;

    DEB_INFO("[%s]: avm_power_open:\n", MODULE_NAME);

    /*-------------------------------------------------------------------------------------------*\
    \*-------------------------------------------------------------------------------------------*/
    open_data = (struct _avm_power_open_data *)kmalloc(sizeof(struct _avm_power_open_data), GFP_KERNEL);
    if(!open_data) {
        DEB_ERR("%s: avm_power_open: open malloc failed\n", MODULE_NAME);
        return -EFAULT;
    }
    memset(open_data, 0, sizeof(*open_data));
    filp->private_data = (void *)open_data;

    DEB_INFO("[%s]: avm_power_open: open success flags=0x%x\n", MODULE_NAME, filp->f_flags);
    return 0;
}

/*-----------------------------------------------------------------------------------------------*\
\*-----------------------------------------------------------------------------------------------*/
static int avm_power_close(struct inode *inode, struct file *filp) {

    DEB_INFO("[%s]: avm_power_close:\n", MODULE_NAME);

    /*--- achtung auf ind wartende "gefreien" und warten bis alle fertig ---*/
    if(filp->private_data) {
        kfree(filp->private_data);
        filp->private_data = NULL;
    }
    return 0;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
ssize_t avm_power_write(struct file *filp, const char *write_buffer, size_t write_length, loff_t *write_pos) {
    char Buffer[256], *p;
    unsigned int org_write_length;
    struct _power_managment_dest_entry *powermodetab = NULL;
    int Mask;
     
    if(write_pos != NULL) {
        DEB_INFO("[%s]: write_length = %u *write_pos = 0x%LX\n", "avm_power_write", write_length, *write_pos);
    }
    org_write_length = write_length;

    if(write_length >= sizeof(Buffer)) {
        write_length = sizeof(Buffer) - 1;
        DEB_NOTE("[avm_power] long line reduce to %u bytes\n", write_length);
    }
    if(filp == NULL) {
        memcpy(Buffer, write_buffer, write_length);
    } else {
        if(copy_from_user(Buffer, write_buffer, write_length)) {
            DEB_ERR("[%s]: avm_power_write: copy_from_user failed\n", "avm_power_write");
            return -EFAULT;
        }
    }
    if(write_pos != NULL) {
        *write_pos += write_length;
    }
    /*--------------------------------------------------------------------------------------*\
    \*--------------------------------------------------------------------------------------*/
    Buffer[write_length] = '\0';
    p = strchr(Buffer, 0x0A);
    if(p) {
        *p = '\0';
        write_length = strlen(Buffer) + 1;
        DEB_NOTE("[avm_power] multi line reduce to %u bytes\n", write_length);
    }
    p = Buffer;

    /*--------------------------------------------------------------------------------------*\
     * cmd extrahieren
    \*--------------------------------------------------------------------------------------*/
    SKIP_SPACES(p);
    DEB_NOTE("[avm_power]: process input \"%s\"\n", p);
    
    /*--------------------------------------------------------------------------------------*\
    \*--------------------------------------------------------------------------------------*/
    if(!strncmp(CMD_MODE, p, sizeof(CMD_MODE) - 1)) {
        p += sizeof(CMD_MODE) - 1;
        if(avm_power_write_check_special_char(&p, '=')) {
            avm_power_writeformaterror("[avm_power] format error: \"MODE = <powermode>\"");
            return -EPERM;
        }
        SKIP_SPACES(p);
        powermodetab = find_powermode(p);
        if(powermodetab == NULL) {
            avm_power_writeformaterror("[avm_power] cant find powermode");
            return -EPERM;
        }
    } else if(!strncmp(WE_CMD_MODE, p, sizeof(WE_CMD_MODE) - 1)) {
        p += sizeof(WE_CMD_MODE) - 1;
        if(avm_power_write_check_special_char(&p, '=')) {
            avm_power_writeformaterror("[avm_power] format error: \"WE_MODE = <hexmask> : Bit 0x10000 gesetzt: nur Maske setzen\"");
            return -EPERM;
        }
        SKIP_SPACES(p);
        sscanf(p, "%x", &Mask);
        if(Mask & 0x10000) {
            WyattEarpMode = Mask;
        } else {
            Wyatt_Earp(Mask);
        }
        return write_length;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
    } else if(!strncmp(CPU_SPEED, p, sizeof(CPU_SPEED) - 1)) {
        p += sizeof(CPU_SPEED) - 1;
        if(avm_power_write_check_special_char(&p, '=')) {
            avm_power_writeformaterror("[avm_power] format error: \"WE_MODE = <hexmask> : Bit 0x10000 gesetzt: nur Maske setzen\"");
            return -EPERM;
        }
        SKIP_SPACES(p);
        sscanf(p, "%x", &Mask);
        if(Mask == 0) {
            int flag = LOCK();
            printk("[speedup] -> 125 MHz\n");
            avm_set_clock(avm_clock_id_system, 125000000);
            *GLOBAL_MISC_ADDR &= ~GLOBAL_MISC_CLOCKSWITCH;
            UNLOCK(flag);
        } else {
            int flag = LOCK();
            printk("[speedup] -> 150 MHz\n");
            avm_set_clock(avm_clock_id_system, 150000000);
            *GLOBAL_MISC_ADDR |= GLOBAL_MISC_CLOCKSWITCH;
            UNLOCK(flag);
        }
        return write_length;
#endif/*--- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) ---*/
    } else {
        avm_power_writeformaterror("[avm_power] format error: \"MODE = <powermode>\"");
        return -EPERM;
    }
    if(powermode_action(powermodetab)) {
        avm_power_writeformaterror("[avm_power] error on activate powermode");
        return -EPERM;
    }
    return write_length;
}

/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
int powermode_action(struct _power_managment_dest_entry *powermodetab) {
    struct _power_managment_clients *registered_client;
    int ret = 0, i = 0;
    while(powermodetab[i].client_name) {
        registered_client = find_pwclient_by_name(powermodetab[i].client_name);
        if(registered_client == NULL) {
            if(powermodetab[i].mandatory) {
                DEB_ERR("[avm_power] '%s' not registered can't execute powermanagment ->stop\n", powermodetab[i].client_name);
                ret = 1;
            } else {
                DEB_WARN("[avm_power] '%s' not registered can't execute powermanagment ->ignore\n", powermodetab[i].client_name);
            }
        } else {
            if(registered_client->CallBackPowerManagmentControl(powermodetab[i].state)) {
                if(powermodetab[i].mandatory) {
                    DEB_ERR("[avm_power] '%s' powermanagment failed->stop\n", powermodetab[i].client_name);
                    ret = 1;
                } else {
                    DEB_WARN("[avm_power] '%s' powermanagment failed->ignore\n", powermodetab[i].client_name);
                }
            }
        }
        if(ret) {
            break;
        }
        i++;
    }
    return ret;
}

/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
void avm_power_writeformaterror(char *text) {
    char TextBuf[256], *p;
    int writesize = sizeof(TextBuf);
    struct _power_managment *powertab = power;

    p = TextBuf;
    snprintf(p, writesize, "%s\navailable powermode:", text);
    writesize -= strlen(p);
    p         += strlen(p);
    while(powertab->powermode) {
        snprintf(p, writesize, "%s%s", powertab->powermode, (powertab+1)->powermode ? "," : "");
        writesize -= strlen(p);
        p         += strlen(p);
        powertab++;
    }
    snprintf(p, writesize, "\n");
    DEB_ERR("%s", TextBuf);
}

/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
struct _power_managment_dest_entry *find_powermode(char *powermode_name) {
    struct _power_managment *powertab = power;

    while(powertab->powermode) {
        if(strcmp(powermode_name, powertab->powermode) == 0) {
            return powertab->dests;
        }
        powertab++;
    }
    return NULL;
}

/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
struct _power_managment_clients *find_pwclient_by_name(char *client_name) {
    struct _power_managment_clients *client = (struct _power_managment_clients *)PwClientAnker;

    while(client) {
        if(strcmp(client_name, client->client_name) == 0) {
            return client;
        }
        client = client->next;
    }
    return NULL; 
}

/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
struct _power_managment_clients *add_pwclient(char *client_name, int (*CallBackPowerManagmentControl)(int state)) {
    struct _power_managment_clients *new;
    int flags;

    new = kmalloc(sizeof(struct _power_managment_clients) + strlen(client_name) + 1, GFP_KERNEL);
    if(new == NULL) {
        return NULL;
    }
    new->client_name = (char *)new + sizeof(struct _power_managment_clients);
    strcpy(new->client_name, client_name);
    new->CallBackPowerManagmentControl = CallBackPowerManagmentControl;
    new->next = NULL;
    flags = LOCK();
    new->next     = (struct _power_managment_clients *)PwClientAnker;
    PwClientAnker = new;
    UNLOCK(flags);
    return new; 
}

/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
void del_pwclient(struct _power_managment_clients *delclient) {
    struct _power_managment_clients *prevclient = NULL; 
    int flags = LOCK();
    struct _power_managment_clients *client = (struct _power_managment_clients *)PwClientAnker;
    while(client) {
        if(client == delclient) {
            if(prevclient == NULL) {
                /*--- erste Element ---*/
                PwClientAnker = client->next;
            } else {
                prevclient->next = client->next;
            }
            UNLOCK(flags);
            kfree(client);
            return;
        }
        prevclient = client;
        client     = client->next;
    }
    UNLOCK(flags);
}

/*-------------------------------------------------------------------------------------*\
 * Powermanagment des Treibers anmelden
 * beide Parameter NULL : dying gasp
 * state
 *  0	Power Off
 *  1	Low Power (kein DSL)
 *  2	Normal Power (slow speed DSL)
 *
 *  10	Full Speed (jetziger Stand)
\*-------------------------------------------------------------------------------------*/
void *PowerManagmentRegister(char *client_name, int (*CallBackPowerManagmentControl)(int state)){
    struct _power_managment_clients *client;
    DEB_INFO("[avm_power] PowerManagmentRegister(\"%s\", 0x%p)\n", client_name, CallBackPowerManagmentControl);

    if(client_name == NULL && CallBackPowerManagmentControl == NULL) {
        /*--- Dying gasp ---*/
        Wyatt_Earp(WyattEarpMode);
        /*--- hier kommt er nie mehr hin ---*/
        return NULL;
    }
    if(client_name == NULL || CallBackPowerManagmentControl == NULL) {
        DEB_ERR("[avm_power]PowerManagmentRegister: invalid param %p %p\n", client_name, CallBackPowerManagmentControl);
        return NULL;
    }
    client = find_pwclient_by_name(client_name);
    if(client) {
        return client;
    }
    return add_pwclient(client_name, CallBackPowerManagmentControl);
}
EXPORT_SYMBOL(PowerManagmentRegister);

/*-------------------------------------------------------------------------------------*\
 * Powermanagment des Treibers abmelden
\*-------------------------------------------------------------------------------------*/
void PowerManagmentRelease(void *Handle){
    struct _power_managment_clients *delclient = (struct _power_managment_clients *)Handle;
    if(Handle == NULL) {
        DEB_ERR("[avm_power]PowerManagmentRelease: invalid Handle\n");
        return;
    }
    del_pwclient(delclient);
}
EXPORT_SYMBOL(PowerManagmentRelease);
