/*------------------------------------------------------------------------------------------*\ * * Copyright (C) 2013 AVM GmbH * * 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 * * Export-Funktionen (fuer Local) PowerManagmentRessourceInfo PowerManagmentActivatePowerMode PowerManagmentRegister PowerManagmentRelease \*------------------------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include "avm_power.h" #if defined(DECTSYNC_PATCH) #include "dectsync.h" #endif/*--- #if defined(DECTSYNC_PATCH) ---*/ static volatile struct _power_managment_clients *PwClientAnker; /*--- #define DEBUG_TRACE_POWERTAB ---*/ #if defined(DEBUG_TRACE_POWERTAB) #define DEB_TRC_PT DEB_ERR #else/*--- #if defined(DEBUG_TRACE_POWERTAB) ---*/ #define DEB_TRC_PT(args...) #endif/*--- #else ---*//*--- #if defined(DEBUG_TRACE_POWERTAB) ---*/ static DEFINE_SPINLOCK(gpw_client_lock); /*-------------------------------------------------------------------------------------*\ * Liste von registrierten Treibern, die den PowermanagmentCallback-Befehl bekommen \*-------------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry dslmode_entries[] = { #if defined(CONFIG_MIPS_UR8) { "speedstep",AVM_PM_CB_IGNORE, 0 }, /*--- kein Aendern der CPU-Frequenz per governor erlauben ---*/ #endif/*--- #if defined(CONFIG_MIPS_UR8) ---*/ { "adsl_event", AVM_PM_CB_UNINSTALLED_OR_FAILED, 10 }, /*--- DSL an: erzeuge Event ---*/ { "adsl", AVM_PM_CB_UNINSTALLED_OR_FAILED, 10 }, /*--- DSL an ---*/ { NULL, 0, 0 } }; /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry atamode_entries[] = { { "adsl_event", AVM_PM_CB_UNINSTALLED_OR_FAILED, 1 }, /*--- DSL komplett aus: erzeuge Event ---*/ { "adsl", AVM_PM_CB_IGNORE, 1 }, /*--- DSL komplett aus ---*/ #if defined(CONFIG_MIPS_UR8) { "speedstep", AVM_PM_CB_IGNORE, 1 }, /*--- Aendern der CPU-Frequenz per governor erlaubt ---*/ #endif/*--- #if defined(CONFIG_MIPS_UR8) ---*/ { NULL, 0, 0 } }; /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry vdslmode_entries[] = { #if defined(CONFIG_MIPS_UR8) { "speedstep", AVM_PM_CB_IGNORE, 0 }, /*--- kein Aendern der CPU-Frequenz per governor erlaubt ---*/ #endif/*--- #if defined(CONFIG_MIPS_UR8) ---*/ { "adsl_event", AVM_PM_CB_UNINSTALLED_OR_FAILED, 10 }, /*--- VDSL oder DSL (FUSIV) an: erzeuge Event ---*/ { "adsl", AVM_PM_CB_IGNORE, 3 }, /*--- (VDSL) OHIO/UR8-DSL aus aber Spannung an ---*/ { NULL, 0, 0 } }; #if defined(CONFIG_MIPS_UR8) /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry powerup_wlan_entries[] = { { "wlanboost", AVM_PM_CB_IGNORE, 0x01 }, /*--- DSP Spannung auf 1.2 V) 7212 ---*/ { NULL, 0, 0 } }; /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry powerdown_wlan_entries[] = { { "wlanboost", AVM_PM_CB_IGNORE, 0x00 }, /*--- DSP Spannung auf 1.2 V) 7212 ---*/ { NULL, 0, 0 } }; #else /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry speedstepstatus_entries[] = { { "speedstep", AVM_PM_CB_IGNORE, 0x80 }, /*--- Speedstep-Sema: per printk ausgeben ---*/ { NULL, 0, 0 } }; #endif/*--- #if defined(CONFIG_MIPS_UR8) ---*/ /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry updatemode_entries[] = { { "speedstep", AVM_PM_CB_IGNORE, 0 }, /*--- kein Aendern der CPU-Frequenz per governor erlaubt ---*/ { NULL, 0, 0 } }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry speedstepon_entries[] = { { "speedstep", AVM_PM_CB_IGNORE, 0x21 }, /*--- Speedstep-Sema: hochzaehlen ---*/ { NULL, 0, 0 } }; static struct _power_managment_dest_entry speedstepoff_entries[] = { { "speedstep", AVM_PM_CB_IGNORE, 0x20 }, /*--- Speedstep-Sema: auf Defaultclock ---*/ { NULL, 0, 0 } }; /*-------------------------------------------------------------------------------------*\ * alles wie gehabt \*-------------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry telefon_profile_off[] = { { "telefon_profile", AVM_PM_CB_FAILED, 0x0 }, /*--- Eventmanager bekommt telefon-profil aus mit ---*/ #if defined(CONFIG_MIPS_UR8) { "speedstep", AVM_PM_CB_IGNORE, 0x11 }, /*--- Aendern der CPU-Frequenz per governor erlaubt ---*/ #endif/*--- #if defined(CONFIG_MIPS_UR8) ---*/ { NULL, 0, 0 } }; /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry telefon_profile_on[] = { { "telefon_profile", AVM_PM_CB_FAILED, 0x1 }, /*--- Eventmanager bekommt telefon-profil an mit ---*/ #if defined(CONFIG_MIPS_UR8) { "speedstep", AVM_PM_CB_IGNORE, 0x10 }, /*--- kein Aendern der CPU-Frequenz per governor erlaubt ---*/ #endif/*--- #if defined(CONFIG_MIPS_UR8) ---*/ { NULL, 0, 0 } }; /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry dect_trace_on[] = { { "protrace", AVM_PM_CB_IGNORE, 0x401 }, /*--- Port 51111 oeffnen ---*/ { "piglet", AVM_PM_CB_IGNORE, 0x401 }, /*--- Port 51112 oeffnen ---*/ { NULL, 0, 0 } }; /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry dect_trace_off[] = { { "protrace", AVM_PM_CB_IGNORE | AVM_PM_ASYNC, 0x400 }, /*--- Port 51111 schliessen ---*/ { "piglet", AVM_PM_CB_IGNORE, 0x400 }, /*--- Port 51112 schliessen ---*/ { NULL, 0, 0 } }; /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry multicast_profile_on[] = { { "telefon_profile", AVM_PM_CB_IGNORE, LOAD_CONTROL_MULTICAST | 0x1 }, /*--- Loadcontrols setzt LOAD_CONTROL_MULTICAST ---*/ { NULL, 0, 0 } }; /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry multicast_profile_off[] = { { "telefon_profile", AVM_PM_CB_IGNORE, LOAD_CONTROL_MULTICAST | 0x0 }, /*--- Loadcontrols ruecksetzen LOAD_CONTROL_MULTICAST ---*/ { NULL, 0, 0 } }; /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry voip_profile_on[] = { { "telefon_profile", AVM_PM_CB_IGNORE, LOAD_CONTROL_VOIPCALL | 0x1 }, /*--- Loadcontrols setzt LOAD_CONTROL_MULTICAST ---*/ { NULL, 0, 0 } }; /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry voip_profile_off[] = { { "telefon_profile", AVM_PM_CB_IGNORE, LOAD_CONTROL_VOIPCALL | 0x0 }, /*--- Loadcontrols ruecksetzen LOAD_CONTROL_MULTICAST ---*/ { NULL, 0, 0 } }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry usbcurrentreq_entries[] = { { "usbpower", AVM_PM_CB_FAILED, 0x2 }, /*--- Abfrage aktueller Stromverbrauch aller USB-Hosts ----*/ { NULL, 0, 0 } }; #if defined(CONFIG_MIPS_UR8) /*-------------------------------------------------------------------------------------*\ * schaltet 5V-Versorgung USB an \*-------------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry usbpoweron_entries[] = { { "usbpower_req", AVM_PM_CB_FAILED, 0x1 }, /*--- Abfragen ob Power on erlaubt ----*/ { "usbpower", AVM_PM_CB_FAILED, 0x1 }, { NULL, 0, 0 } }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry usbpoweroff_entries[] = { { "usbpower_req", AVM_PM_CB_FAILED, 0x0 }, /*--- Abfrage ob Power off erlaubt ----*/ { "usbpower", AVM_PM_CB_FAILED, 0x0 }, { NULL, 0, 0 } }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry powerdown_isdnab_entries[] = { { "piglet", AVM_PM_CB_FAILED, 0x100 }, /*--- 40 V ausschalten ---*/ { NULL, 0, 0 } }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry powerup_isdnab_entries[] = { { "piglet", AVM_PM_CB_FAILED, 0x101 }, /*--- 40 V anschalten ---*/ { NULL, 0, 0 } }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry powerdown_slic1_entries[] = { { "isdn", AVM_PM_CB_FAILED, 0x10 }, /*--- ueber PCM-Register ausschalten ---*/ { NULL, 0, 0 } }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry powerup_slic1_entries[] = { { "isdn", AVM_PM_CB_FAILED, 0x11 }, /*--- ueber PCM-Register anschalten ---*/ { NULL, 0, 0 } }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry powerdown_slic2_entries[] = { { "isdn", AVM_PM_CB_FAILED, 0x20 }, /*--- ueber PCM-Register ausschalten ---*/ { NULL, 0, 0 } }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry powerup_slic2_entries[] = { { "isdn", AVM_PM_CB_FAILED, 0x21 }, /*--- ueber PCM-Register anschalten ---*/ { NULL, 0, 0 } }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry speedstep_status_entries[] = { { "speedstep",AVM_PM_CB_UNINSTALLED_OR_FAILED, 0x40 }, /*--- Speedstep Status abfragen ! ---*/ { NULL, 0, 0 } }; #endif/*--- #if defined(CONFIG_MIPS_UR8) ---*/ /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry pcmlinkbus_stop_entries[] = { { "pcmlink", AVM_PM_CB_UNINSTALLED_OR_FAILED, 0x0 }, /*--- PCM-Bus deaktivieren ---*/ { "speedstep",AVM_PM_CB_IGNORE, 0x20 }, /*--- Speedstep aus: anschliessend noch Status-Abfrage notwendig (da asynchron) ! ---*/ { NULL, 0, 0 } }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry pcmlinkbus_start_entries[] = { { "pcmlink", AVM_PM_CB_UNINSTALLED_OR_FAILED, 0x1 }, /*--- PCM-Bus reaktivieren ---*/ { "speedstep",AVM_PM_CB_IGNORE, 0x21 }, /*--- Speedstep reaktivieren ---*/ { NULL, 0, 0 } }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry pots_load_entries[] = { { "piglet", AVM_PM_CB_UNINSTALLED_OR_FAILED, 0x200 }, /*--- checke ob 2 Bitfiles ueberhaupt existent ---*/ { "piglet", AVM_PM_CB_UNINSTALLED_OR_FAILED, 0x203 }, /*--- nur wenn TE aktuell Wechsel notwendig ---*/ { "pcmlink", AVM_PM_CB_UNINSTALLED_OR_FAILED, 0x0 }, /*--- PCM-Bus deaktivieren ---*/ { "piglet", AVM_PM_CB_IGNORE, 0x210 }, /*--- lade POTS-File ---*/ { "pcmlink", AVM_PM_CB_UNINSTALLED_OR_FAILED, 0x1 }, /*--- PCMBus reaktivieren ---*/ { "isdn", AVM_PM_CB_UNINSTALLED_OR_FAILED, 0x100 }, /*--- TE-DKanal aus (inkl. Slot austragen) ---*/ { NULL, 0, 0 } }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry te_load_entries[] = { { "piglet", AVM_PM_CB_UNINSTALLED_OR_FAILED, 0x200 }, /*--- checke ob 2 Bitfiles ueberhaupt existent ---*/ { "piglet", AVM_PM_CB_UNINSTALLED_OR_FAILED, 0x202 }, /*--- nur wenn POTS aktuell Wechsel notwendig ---*/ { "pcmlink", AVM_PM_CB_UNINSTALLED_OR_FAILED, 0x0 }, /*--- PCM-Bus deaktivieren ---*/ { "piglet", AVM_PM_CB_IGNORE, 0x211 }, /*--- lade TE-File ---*/ { "pcmlink", AVM_PM_CB_UNINSTALLED_OR_FAILED, 0x1 }, /*--- PCMBus reaktivieren ---*/ { NULL, 0, 0 } }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry te_reload_entries[] = { { "piglet", AVM_PM_CB_UNINSTALLED_OR_FAILED, 0x200 }, /*--- checke ob 2 Bitfiles ueberhaupt existent ---*/ { "piglet", AVM_PM_CB_UNINSTALLED_OR_FAILED, 0x203 }, /*--- nur wenn TE aktuell reload notwendig ---*/ { "pcmlink", AVM_PM_CB_UNINSTALLED_OR_FAILED, 0x0 }, /*--- PCM-Bus deaktivieren ---*/ { "piglet", AVM_PM_CB_IGNORE, 0x213 }, /*--- lade TE-File (force) ---*/ { "pcmlink", AVM_PM_CB_UNINSTALLED_OR_FAILED, 0x1 }, /*--- PCMBus reaktivieren ---*/ { "isdn", AVM_PM_CB_UNINSTALLED_OR_FAILED, 0x100 }, /*--- TE-DKanal komplett aus -> triggert Reinitialisierung ---*/ { NULL, 0, 0 } }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry pots_reload_entries[] = { { "piglet", AVM_PM_CB_UNINSTALLED_OR_FAILED, 0x200 }, /*--- checke ob 2 Bitfiles ueberhaupt existent ---*/ { "piglet", AVM_PM_CB_UNINSTALLED_OR_FAILED, 0x202 }, /*--- nur wenn POTS aktuell reload notwendig ---*/ { "pcmlink", AVM_PM_CB_UNINSTALLED_OR_FAILED, 0x0 }, /*--- PCM-Bus deaktivieren ---*/ { "piglet", AVM_PM_CB_IGNORE, 0x212 }, /*--- lade POTS-File (force) ---*/ { "pcmlink", AVM_PM_CB_UNINSTALLED_OR_FAILED, 0x1 }, /*--- PCMBus reaktivieren ---*/ { NULL, 0, 0 } }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry tepots_switchauto_entries[] = { { "piglet", AVM_PM_CB_UNINSTALLED_OR_FAILED, 0x204 }, /*--- AutoMode an ---*/ { NULL, 0, 0 } }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry tepots_switchmanu_entries[] = { { "piglet", AVM_PM_CB_UNINSTALLED_OR_FAILED, 0x205 }, /*----AutoMode aus ---*/ { NULL, 0, 0 } }; /*--------------------------------------------------------------------------------*\ * wird vom pcmlink-Treiber ausgewertet: \*--------------------------------------------------------------------------------*/ static struct _power_managment_dest_entry teactive_entries[] = { { "piglet", AVM_PM_CB_FAILED, 0x201 }, { NULL, 0, 0 } }; /*-------------------------------------------------------------------------------------*\ * powermode mit nachfolgender Powermanagment-Treiber-Liste \*-------------------------------------------------------------------------------------*/ static struct _power_managment power[] = { { "dsl", dslmode_entries, PM_ACESS_ALL}, { "ata", atamode_entries, PM_ACESS_ALL}, { "vdsl", vdslmode_entries, PM_ACESS_ALL}, /*--- im VDSL-Mode DSL auf ATA stellen ---*/ { "update", updatemode_entries, PM_ACESS_ALL}, { "speedstep_on", speedstepon_entries, PM_ACESS_APPL}, { "speedstep_off", speedstepoff_entries, PM_ACESS_APPL}, { "dect_trace_on", dect_trace_on, PM_ACESS_ALL}, /*--- DECT-Trace Ports bereitstellen ---*/ { "dect_trace_off", dect_trace_off, PM_ACESS_ALL}, /*--- DECT-Trace Ports schliessen ---*/ { "telefon_profile_on", telefon_profile_on, PM_ACESS_ALL}, /*--- schalte auf normalspeed - niemals runter ---*/ { "telefon_profile_off", telefon_profile_off, PM_ACESS_ALL}, /*--- vorhergehendes Profil ---*/ { "multicast_profile_on", multicast_profile_on, PM_ACESS_ALL}, /*--- signalisiert das Multicast an ---*/ { "multicast_profile_off", multicast_profile_off, PM_ACESS_ALL}, /*--- signalisiert, dass Multicast aus ---*/ { "voip_profile_on", voip_profile_on, PM_ACESS_ALL}, /*--- signalisiert das Voip an ---*/ { "voip_profile_off", voip_profile_off, PM_ACESS_ALL}, /*--- signalisiert, dass Voip aus ---*/ #if defined(CONFIG_MIPS_UR8) { "usb_poweron", usbpoweron_entries, PM_ACESS_ALL}, { "usb_poweroff", usbpoweroff_entries, PM_ACESS_ALL}, { "powerdown_isdnab", powerdown_isdnab_entries, PM_ACESS_ALL}, { "powerup_isdnab", powerup_isdnab_entries, PM_ACESS_ALL}, { "powerdown_slic1", powerdown_slic1_entries, PM_ACESS_ALL}, { "powerup_slic1", powerup_slic1_entries, PM_ACESS_ALL}, { "powerdown_slic2", powerdown_slic2_entries, PM_ACESS_ALL}, { "powerup_slic2", powerup_slic2_entries, PM_ACESS_ALL}, { "wlan_booston", powerup_wlan_entries, PM_ACESS_ALL}, /*--- anheben der Spannung 7212 auf 1.2V ---*/ { "wlan_boostoff", powerdown_wlan_entries, PM_ACESS_ALL}, { "speedstep_status", speedstep_status_entries, PM_ACESS_ALL}, #else { "speedstep_status", speedstepstatus_entries, PM_ACESS_APPL}, #endif/*--- #if defined(CONFIG_MIPS_UR8) ---*/ { "usb_current_req", usbcurrentreq_entries, PM_ACESS_ALL}, { "pcmlink_bus_off", pcmlinkbus_stop_entries, PM_ACESS_DRIVER}, { "pcmlink_bus_on", pcmlinkbus_start_entries, PM_ACESS_DRIVER}, { "pots_load", pots_load_entries, PM_ACESS_ALL}, { "te_load", te_load_entries, PM_ACESS_ALL}, { "te_reload", te_reload_entries, PM_ACESS_ALL}, { "pots_reload", pots_reload_entries, PM_ACESS_ALL}, { "tepots_switchauto", tepots_switchauto_entries, PM_ACESS_ALL}, { "tepots_switchmanu", tepots_switchmanu_entries, PM_ACESS_ALL}, { "te_active", teactive_entries, PM_ACESS_ALL}, /*--- die letzten werden die ersten sein (wird alle 8 msec aufgerufen) ---*/ { NULL, NULL, 0} }; /*-------------------------------------------------------------------------------------*\ * access: Zugriff vom Treiber/Applikation PM_ACESS_DRIVER oder PM_ACESS_APPL \*-------------------------------------------------------------------------------------*/ struct _power_managment_dest_entry *find_powermode(char *powermode_name, unsigned int access) { struct _power_managment *powertab = power; while(powertab->powermode) { if((strcmp(powermode_name, powertab->powermode) == 0) && (powertab->Access & access)) { /*--- DEB_ERR("[avm_power] exec powermanagment '%s'\n", powertab->powermode); ---*/ return powertab->dests; } powertab++; } return NULL; } /*--------------------------------------------------------------------------------*\ * ret: 0 freigegeben \*--------------------------------------------------------------------------------*/ static inline int put_pwclient(struct _power_managment_clients *client) { if(atomic_read(&client->link) == 0) { printk(KERN_ERR"%s error link already zero\n", __func__); return 0; } if (atomic_dec_and_test(&client->link)) { /*--- printk(KERN_INFO"%s free\n", __func__); ---*/ kfree(client); return 0; } return 1; } /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ struct _power_managment_clients *get_pwclient_by_name(char *client_name) { struct _power_managment_clients *client; unsigned long flags; spin_lock_irqsave(&gpw_client_lock, flags); client = (struct _power_managment_clients *)PwClientAnker; while(client) { if(strcmp(client_name, client->client_name) == 0) { atomic_add(1, &client->link); spin_unlock_irqrestore(&gpw_client_lock, flags); return client; } client = client->next; } spin_unlock_irqrestore(&gpw_client_lock, flags); return NULL; } /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ static struct _power_managment_clients *add_pwclient(char *client_name, int (*CallBackPowerManagmentControl)(int state)) { struct _power_managment_clients *new; unsigned long flags; new = kmalloc(sizeof(struct _power_managment_clients) + strlen(client_name) + 1, GFP_KERNEL); if(new == NULL) { return NULL; } atomic_set(&new->link, 1); new->client_name = (char *)new + sizeof(struct _power_managment_clients); strcpy(new->client_name, client_name); new->CallBackPowerManagmentControl = CallBackPowerManagmentControl; spin_lock_irqsave(&gpw_client_lock, flags); new->next = (struct _power_managment_clients *)PwClientAnker; PwClientAnker = new; spin_unlock_irqrestore(&gpw_client_lock, flags); return new; } /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ static int del_pwclient(struct _power_managment_clients *delclient) { unsigned long flags; struct _power_managment_clients *prevclient = NULL; struct _power_managment_clients *client; spin_lock_irqsave(&gpw_client_lock, flags); client = (struct _power_managment_clients *)PwClientAnker; while(client) { if(client == delclient) { if(prevclient == NULL) { /*--- erste Element ---*/ PwClientAnker = client->next; } else { prevclient->next = client->next; } spin_unlock_irqrestore(&gpw_client_lock, flags); return put_pwclient(client); } prevclient = client; client = client->next; } spin_unlock_irqrestore(&gpw_client_lock, flags); return 0; } /*-------------------------------------------------------------------------------------*\ * Powermanagment des Treibers anmelden * beide Parameter NULL : dying gasp * state: kontextbezogen - siehe (linux_)avm_power.h \*-------------------------------------------------------------------------------------*/ 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) { DEB_ERR("[avm_power]PowerManagmentRegister: invalid param %p %p\n", client_name, CallBackPowerManagmentControl); return NULL; } client = get_pwclient_by_name(client_name); if(client) { put_pwclient(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; printk(KERN_ERR"[avm_power] PowerManagmentRelease(0x%p)\n", Handle); if(Handle == NULL) { DEB_ERR("[avm_power]%s: invalid Handle\n", __func__); return; } if(del_pwclient(delclient)) { schedule_timeout(HZ/10); /*--- er scheint noch in CallBackPowerManagmentControl zu haengen: hier warten ! ---*/ } } EXPORT_SYMBOL(PowerManagmentRelease); /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ struct _async_powermodetab { /*--- struct work_struct work; ---*/ struct delayed_work delayed_wk; struct _power_managment_dest_entry *powermodetab; }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void async_powermode_action(struct work_struct *work) { /*--- struct _async_powermodetab *papmt = container_of(work, struct _async_powermodetab, work); ---*/ struct _async_powermodetab *papmt = container_of(work, struct _async_powermodetab, delayed_wk.work); powermode_action(papmt->powermodetab, 1); /*--- DEB_ERR("[avm_power]%s: free: %p\n", __func__, papmt); ---*/ kfree(papmt); } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ int powermode_action_async(struct _power_managment_dest_entry *powermodetab) { struct _async_powermodetab *papmt; papmt = kzalloc(sizeof(struct _async_powermodetab), GFP_ATOMIC); if (papmt == NULL) { DEB_ERR("[avm_power]%s: no memory\n", __func__); return 1; } papmt->powermodetab = powermodetab; /*--- INIT_WORK(&papmt->work, async_powermode_action); ---*/ INIT_DELAYED_WORK(&papmt->delayed_wk, async_powermode_action); /*--- DEB_ERR("[avm_power]%s: async mode: %p\n", __func__, papmt); ---*/ /*--- schedule_work(&papmt->work); ---*/ schedule_delayed_work(&papmt->delayed_wk, HZ / 100); return 0; } /*-------------------------------------------------------------------------------------*\ * hier werden die Tabelleneintraege (struct _power_managment_dest_entry ...[]) * "ausgeführt" \*-------------------------------------------------------------------------------------*/ int powermode_action(struct _power_managment_dest_entry *powermodetab, unsigned int async_context) { struct _power_managment_clients *registered_client; int ret = 0, i = 0, locked = 0; unsigned long flags; if(powermodetab == NULL) { return 1; } while(powermodetab[i].client_name) { registered_client = get_pwclient_by_name(powermodetab[i].client_name); if(registered_client == NULL) { if(powermodetab[i].mandatory & AVM_PM_CB_UNINSTALLED_OR_FAILED) { DEB_TRC_PT("[avm_power] '%s' not registered can't execute powermanagment ->stop\n", powermodetab[i].client_name); ret = 1; } else { DEB_TRC_PT("[avm_power] '%s' not registered can't execute powermanagment ->ignore\n", powermodetab[i].client_name); } } else { if((async_context == 0) && (powermodetab[i].mandatory & AVM_PM_ASYNC)) { put_pwclient(registered_client); return powermode_action_async(&powermodetab[i]); } if((powermodetab[i].mandatory & AVM_PM_LOCK) && (locked == 0)) { locked = 1; flags = avm_power_lock(); } if((ret = registered_client->CallBackPowerManagmentControl(powermodetab[i].state))) { if(powermodetab[i].mandatory & (AVM_PM_CB_UNINSTALLED_OR_FAILED | AVM_PM_CB_FAILED)) { DEB_TRC_PT("[avm_power] '%s'=0x%x powermanagment failed->stop ret=%x\n", powermodetab[i].client_name, powermodetab[i].state, ret); } else { DEB_TRC_PT("[avm_power] '%s'=0x%x powermanagment failed->ignore ret=%x\n", powermodetab[i].client_name, powermodetab[i].state, ret); ret = 0; } } else { /*--- DEB_TRC_PT("[avm_power] '%s'=0x%x powermanagment ok\n", powermodetab[i].client_name, powermodetab[i].state); ---*/ } put_pwclient(registered_client); } if(ret) { break; } i++; } if(locked) { avm_power_unlock(flags); } return ret; } /*--------------------------------------------------------------------------------*\ * Powermanagment nicht ueber Tabelle * ret 0: alles ok, 1: Callback-Fehler, 2: Client nicht registriert * \*--------------------------------------------------------------------------------*/ int powermode_action_nolist(char *client_name, int State) { struct _power_managment_clients *registered_client; int ret; registered_client = get_pwclient_by_name(client_name); if(registered_client == NULL) { return 2; } ret = registered_client->CallBackPowerManagmentControl(State); put_pwclient(registered_client); return ret ? 1 : 0; } /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ 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) { if(powertab->Access & PM_ACESS_APPL) { 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); } /*-------------------------------------------------------------------------------------*\ * vom Kernel den Powermode ändern * Returnwert: 0 ok sonst Abbruch mit Fehler \*-------------------------------------------------------------------------------------*/ int PowerManagmentActivatePowerMode(char *powermode_name){ struct _power_managment_dest_entry *powermodetab = find_powermode(powermode_name, PM_ACESS_DRIVER); #if defined(DEBUG_TRACE_POWERTAB) if(powermodetab != teactive_entries) { DEB_TRC_PT("[avm_power]PowerManagmentActivatePowerMode: '%s'\n", powermode_name); } #endif return powermode_action(powermodetab, 0); } EXPORT_SYMBOL(PowerManagmentActivatePowerMode); #ifdef CONFIG_AVM_POWERMETER /*--------------------------------------------------------------------------------*\ * die Infos werden erst hier zusammengesammelt, Status wird mit Set/Reset-Flag * signalisiert, Power wird hier gesetzt \*--------------------------------------------------------------------------------*/ static void convert_isdnpowerstate(unsigned int *SaveValue, unsigned int Value) { int i; /*--- Ebene1/3 State uebernehmen ---*/ if(Value & (1 << 31)){ /* Set State */ *SaveValue |= Value; } else { /* Reset State */ *SaveValue &= ~Value; } *SaveValue &= ~0xFFFF; /*--- Powerratebereich loeschen ---*/ for(i = 1; i < 5; i++) { /*--- max. 4 Controller E1 aktiv -> power++ ! ---*/ if(*SaveValue & PM_E1STATUS(i)) { *SaveValue += 100; } } /*--- DBG_ERR(("convert_isdnpowerstate: %x\n", *SaveValue)); ---*/ } #define LOAD_INT(x) ((x) >> FSHIFT) #define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100) /*--------------------------------------------------------------------------------*\ * aktuell max. 4 CPU's \*--------------------------------------------------------------------------------*/ static inline unsigned int cpusloadrate_to_powerrate(unsigned int cpuspowerrate) { unsigned int cpu, norm = 0; unsigned int run = 0; for(cpu = 0; cpu < NR_CPUS; cpu++) { if (cpu_online(cpu)) { norm++; run += cpuspowerrate & 0xFF; } cpuspowerrate >>= 8; } if(norm == 0) { return norm; } return run / norm; } /*--------------------------------------------------------------------------------*\ * aktuell max. 4 CPU's \*--------------------------------------------------------------------------------*/ static char *display_per_cpurate(char *txt, int txt_len, unsigned int cpuspowerrate) { unsigned int cpu, len, run; char idle_txt[64], *pidle_txt = idle_txt; char run_txt[64], *prun_txt = run_txt; unsigned int idle_txt_len = sizeof(idle_txt); unsigned int run_txt_len = sizeof(run_txt); idle_txt[0] = 0; run_txt[0] = 0; for(cpu = 0; cpu < NR_CPUS; cpu++) { if(cpu_online(cpu)) { run = cpuspowerrate & 0xFF; if(idle_txt_len) { len = snprintf(pidle_txt, idle_txt_len, "%s%u", idle_txt[0] ? "/" : "", 100 - run); idle_txt_len -= len, pidle_txt += len; } if(run_txt_len) { len = snprintf(prun_txt, run_txt_len, "%s%u", run_txt[0] ? "/" : "", run); run_txt_len -= len, prun_txt += len; } } cpuspowerrate >>= 8; } snprintf(txt, txt_len, "idle: %s %% (%s %%)", idle_txt, run_txt); return txt; } /*--------------------------------------------------------------------------------*\ * Funktion wird von Treibern aufgerufen um Infos ueber den aktuellen Power-Status zu liefern \*--------------------------------------------------------------------------------*/ int PowerManagmentRessourceInfo(enum _powermanagment_device device, int power_rate){ int changes = 0, ready; unsigned int cpus_loadrate; if((unsigned)device >= powerdevice_maxdevices) { DEB_ERR("[avm_power]PowerManagmentRessourceInfo: unknown device: %d\n", device); return 0; } ready = pm_ressourceinfo.kthread ? 1 : 0; #if defined(DECTSYNC_PATCH) if(device == powerdevice_dectsync) { if(ready == 0) { return 0; } if(power_rate < 0) { if(power_rate == -1) { /*--- Piglet sagt wir haben DECT ---*/ avm_power_dect_syncmode(0);/*--- im Fall der Telefonie: nicht auf Run anderer achten! ---*/ start_dectsync(1, 0); } else if(power_rate == -2) { /*--- stop dectsync ---*/ idle_dectsynchandler(1); } else if(power_rate == -3) { avm_power_dect_syncmode(1); /*--- im Fall der Telefonie: auf Run anderer achten! ---*/ start_dectsync(1, 0); } else { display_dectsync(pm_ressourceinfo.loadcntrl); } power_rate = 0; } if(power_rate == 0) { /*--- Indikator das jetzt im Idle-Kontext ---*/ return idle_dectsynchandler(0); } return pm_ressourceinfo.deviceinfo[powerdevice_loadrate].power_rate; } #endif/*--- #if defined(DECTSYNC_PATCH) ---*/ if(device == powerdevice_loadrate) { cpus_loadrate = power_rate; power_rate = cpusloadrate_to_powerrate(cpus_loadrate); pm_ressourceinfo.loadcntrl = avm_powermanager_load_control_handler(power_rate); #if defined(DECTSYNC_PATCH) { int wastepercent = updaterun_dectsync(power_rate); if(wastepercent < power_rate) { /*--- Korrektur der power-rate ---*/ power_rate -= wastepercent; } } #endif/*--- #if defined(DECTSYNC_PATCH) ---*/ } if(pm_ressourceinfo.deviceinfo[device].power_rate != power_rate) { if((device == powerdevice_isdnte) || (device == powerdevice_isdnnt)) { convert_isdnpowerstate(&pm_ressourceinfo.deviceinfo[device].power_rate, power_rate); } else { pm_ressourceinfo.deviceinfo[device].power_rate = power_rate; } if((avm_power_disp_loadrate & 1) && (device == powerdevice_loadrate)) { #if defined(DECTSYNC_PATCH) if(display_dectsync(pm_ressourceinfo.loadcntrl)) { #endif/*--- #else ---*//*--- #if defined(DECTSYNC_PATCH) ---*/ char txt[64]; unsigned long avnrun[3]; get_avenrun(avnrun, FIXED_1/200, 0); printk(KERN_ERR"%s loadavg %lu.%lu %lu.%lu %lu.%lu loadcntrl 0x%x\n", display_per_cpurate(txt, sizeof(txt), cpus_loadrate), LOAD_INT(avnrun[0]), LOAD_FRAC(avnrun[0]), LOAD_INT(avnrun[1]), LOAD_FRAC(avnrun[1]), LOAD_INT(avnrun[2]), LOAD_FRAC(avnrun[2]), pm_ressourceinfo.loadcntrl ); #if defined(DECTSYNC_PATCH) } #endif/*--- #if defined(DECTSYNC_PATCH) ---*/ } changes = (ready && pm_ressourceinfo.NormP) ? 1 : 0; /*--- nur wenn Thread existiert und script geladen ----*/ pm_ressourceinfo.Changes += changes; if((avm_power_disp_loadrate & 2) && (device != powerdevice_loadrate)) printk(KERN_ERR"[avm_power]PowerManagmentRessourceInfo: device: %s value=(0x%x)%d changes=%d\n", pm_name_device(device), power_rate, power_rate, pm_ressourceinfo.Changes); } if(changes) { wake_up_interruptible(&pm_ressourceinfo.wait_queue); } return 0; } EXPORT_SYMBOL(PowerManagmentRessourceInfo); #endif/*--- #ifdef CONFIG_AVM_POWERMETER ---*/