view src/cs/drivers/drv_app/lcc/lcc_task.c @ 303:f76436d19a7a default tip

!GPRS config: fix long-standing AT+COPS chance hanging bug There has been a long-standing bug in FreeCalypso going back years: sometimes in the AT command bring-up sequence of an ACI-only MS, the AT+COPS command would produce only a power scan followed by cessation of protocol stack activity (only L1 ADC traces), instead of the expected network search sequence. This behaviour was seen in different FC firmware versions going back to Citrine, and seemed to follow some law of chance, not reliably repeatable. This bug has been tracked down and found to be specific to !GPRS configuration, stemming from our TCS2/TCS3 hybrid and reconstruction of !GPRS support that was bitrotten in TCS3.2/LoCosto version. ACI module psa_mms.c, needed only for !GPRS, was missing in the TCS3 version and had to be pulled from TCS2 - but as it turns out, there is a new field in the MMR_REG_REQ primitive that needs to be set correctly, and that psa_mms.c module is the place where this initialization needed to be added.
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 08 Jun 2023 08:23:37 +0000
parents 4e78acac3d88
children
line wrap: on
line source

/******************************************************************************
 * Power Task (pwr)
 * Design and coding by Svend Kristian Lindholm, skl@ti.com
 *
 * Main PWR Task
 *
 * $Id: pwr_task.c 1.1 Wed, 20 Aug 2003 10:22:37 +0200 skl $
 *
 ******************************************************************************/

#include "lcc/lcc.h"
#include "lcc/lcc_task.h"
#include "lcc/lcc_handle_message.h"
#include "lcc/lcc_tm_i.h"
#include "lcc/lcc_trace.h"

#include "rv/rv_defined_swe.h"

#include "ffs/ffs.h"

#include <string.h>
/******************************************************************************
 * Globals and function prototypes
 ******************************************************************************/

extern T_PWR_CTRL_BLOCK *pwr_ctrl;
extern T_PWR_CFG_BLOCK *pwr_cfg;

// Event handling functions
T_RV_RET process_spi_adc_indication             (T_PWR_REQ *request);

// Timer event handling functions
T_RV_RET process_pwr_handle_T1_expiration       (T_PWR_REQ *request);
T_RV_RET process_pwr_handle_T2_expiration       (T_PWR_REQ *request);
T_RV_RET process_pwr_handle_T3_expiration       (T_PWR_REQ *request);
T_RV_RET process_pwr_handle_T4_expiration       (T_PWR_REQ *request);
T_RV_RET process_pwr_handle_mod_cycle_expiration(T_PWR_REQ *request);
T_RV_RET process_pwr_handle_mmi_info_expiration (T_PWR_REQ *request);

// Interrupt event handling functions
T_RV_RET process_abb_chg_unplugged_ind          (T_PWR_REQ *request);


T_RVM_RETURN pwr_check_files(void);
T_RVM_RETURN pwr_read_files(void);
T_RVM_RETURN pwr_read_chg_files(void);
T_RVM_RETURN pwr_read_cal_files(void);

void build_name(const char *ch_pre, char *cfg_id , UINT8 index, const char * ch_post,  char * name);

// FFS function prototypes
effs_t ffs_stat(const char *name, struct stat_s *stat);
int ffs_fread(const char *name, void *addr, int size);

void ttr(unsigned trmask, char *format, ...);
void str(unsigned mask, char *string);


/******************************************************************************
 * PWR Task
 ******************************************************************************/

// This function checks the existance of FFS directories and files related
// to the PWR module - See RD818
// If the existance of the object is MANDATORY pwr_check_files returns an 
// error and the PWR configuration is stopped
// If the existance of the object is OPTIONAL pwr_check_files a
// warning is given and the PWR configuration proceeds

T_RVM_RETURN pwr_check_files()
{
    T_FFS_SIZE error;
    T_FFS_STAT stat;

    ttw(ttr(TTrInit, "pwr_check_files(%d)" NL, 0));

    // Check directories:
    // /pwr                       MANDATORY
    // /pwr/bat                   MANDATORY
    // /pwr/chg                   OPTIONAL
    // /mmi                       OPTIONAL
    // /mmi/pwr                   OPTIONAL

    // MANDATORY directories
    if ((error = ffs_stat("/pwr", &stat)) == EFFS_OK) {
        if (stat.type != OT_DIR) {
           ttr(TTrFatal, "pwr exists but is not a directory %d" NL, 0);
           return EFFS_NOTADIR;
        }
    } else {
        ttr(TTrFatal, "no /pwr directory %d" NL, 0);
        return error;
    }
    if ((error = ffs_stat("/pwr/bat", &stat)) == EFFS_OK) {
        if (stat.type != OT_DIR) {
           ttr(TTrFatal, "/pwr/bat exists but is not a directory %d" NL, 0);
           return EFFS_NOTADIR;
        }
    } else {
        ttr(TTrFatal, "no /pwr/bat directory %d" NL, 0);
        return error;
    }


    // OPTIONAL directories
    if ((error = ffs_stat("/pwr/chg", &stat)) == EFFS_OK) {
        if (stat.type != OT_DIR) {
           ttr(TTrWarning, "/pwr/chg exists but is not a directory %d" NL, 0);
        }
    } else {
        ttr(TTrWarning, "no /pwr/chg directory %d" NL, 0);
    }
    if ((error = ffs_stat("/mmi", &stat)) == EFFS_OK) {
        if (stat.type != OT_DIR) {
           ttr(TTrWarning, "/mmi exists but is not a directory %d" NL, 0);
        }
    } else {
        ttr(TTrWarning, "no /mmi directory %d" NL, 0);
    }
    if ((error = ffs_stat("/mmi/pwr", &stat)) == EFFS_OK) {
        if (stat.type != OT_DIR) {
           ttr(TTrWarning, "/mmi/pwr exists but is not a directory %d" NL, 0);
        }
    } else {
        ttr(TTrWarning, "no /mmi/pwr directory %d" NL, 0);
    }

    // Check calibration files:
    // /pwr/vbat.cal              MANDATORY
    // NOT checked - it MUST be present - else we will have no Vbat measurements

    // Check configuration files:
    // /pwr/common.cfg            MANDATORY
    // /pwr/bat/bat<N>.cfg        MANDATORY
    // /pwr/bat/temp<N>.cfg       OPTIONAL
    // /pwr/chg/chg<N>.cfg        OPTIONAL


    // MANDATORY files
    if ((error = ffs_stat("/pwr/common.cfg", &stat)) == EFFS_OK) {
        if (stat.type != OT_FILE) {
           ttr(TTrFatal, "/pwr/common.cfg exists but is not a file %d" NL, 0);
           return EFFS_NOTADIR;
        }
    } else {
        ttr(TTrFatal, "no /pwr/common.cfg file %d" NL, 0);
        return error;
    }

    ttw(ttr(TTrInit, "pwr_check_files(%d)" NL, 0xFF));
    return RV_OK;
}

// This function reads the FFS pwr configuration files
//                               /pwr/vbat.cal          MANDATORY
//                               /mmi/pwr/bsie          OPTIONAL
//                               /pwr/common.cfg        MANDATORY
//                               /pwr/bat/bat<n>.cfg    MANDATORY
//                               /pwr/bat/temp<n>.cfg   MANDATORY
//
// Precondition: Files have been checked with pwr_check_files()
//               Therefore we know they exist. Charger files are read later.
//

T_RVM_RETURN pwr_read_files()
{
    T_FFS_SIZE error;
    T_FFS_STAT stat;
    char name[20];
    uint8 i;
    char cfg_id;

    ttw(ttr(TTrInit, "pwr_read_files(%d)" NL, 0));


    // Brute force of charger configuration ? /pwr/chg/force
    if ((error = ffs_stat("/pwr/chg/force", &stat)) == EFFS_OK) {
        error = ffs_fread("/pwr/chg/force", &pwr_cfg->data.cforce, 1);
        ttw(ttr(TTrInitLow, "Read /pwr/chg/force(%d)" NL, error));
        pwr_cfg->data.chg_cfg_id = pwr_cfg->data.cforce + '0';
        pwr_ctrl->chg_cfg_id     = pwr_cfg->data.cforce;
    } else {
        // Use 'normal' 'plug & play' configuration
        pwr_cfg->data.cforce = 0;
        pwr_cfg->data.chg_cfg_id = 1 + '0'; // Default
        pwr_ctrl->chg_cfg_id     = 1;       // Default
    }

    // Brute force of battery configuration ? /pwr/bat/force
    if ((error = ffs_stat("/pwr/bat/force", &stat)) == EFFS_OK) {
        error = ffs_fread("/pwr/bat/force", &pwr_cfg->data.bforce, 1);
        ttw(ttr(TTrInitLow, "Read /pwr/bat/force(%d)" NL, error));
        pwr_ctrl->cfg_id     = pwr_cfg->data.bforce;
        pwr_cfg->data.cfg_id = pwr_cfg->data.bforce + '0';
    } else {
        // Use 'normal' 'plug & play' configuration
        // Precondition: We have a reliable battery id measurement
        pwr_cfg->data.bforce = 0;
        ttw(ttr(TTrInitLow, "Plug & play bat_id=%d" NL, pwr_cfg->data.bat_id));
    }

    // Read /pwr/common.cfg
    error = ffs_fread("/pwr/common.cfg", &pwr_cfg->common, PWR_COMMON_CFG_SIZE);
    ttw(ttr(TTrInitLow, "Read /pwr/common.cfg(%d)" NL, error));

    // Read /mmi/pwr/bsie 
    // Apply defaults if file doesn't exist
    if ((error = ffs_stat("/mmi/pwr/bsie.cfg", &stat)) == EFFS_OK) {
        if (stat.type != OT_FILE) {
           ttr(TTrWarning, "/mmi/pwr/bsie.cfg exists but is not a file %d" NL, 0);
           return EFFS_NOTAFILE;
        } else {
           error = ffs_fread("/mmi/pwr/bsie.cfg", &pwr_cfg->mmi, sizeof(pwr_cfg->mmi));
           ttw(ttr(TTrInitLow, "Read /mmi/pwr/bsie.cfg(%d)" NL, error));
        }
    } else {
        ttr(TTrWarning, "no /mmi/pwr/bsie file %d" NL, 0);
        // Apply defaults
        pwr_cfg->mmi.repetition = PWR_MMI_REP_THR;
    }

    if (pwr_cfg->data.bforce > 0) {
        // Brute force battery configuration
        build_name("/pwr/bat/bat", &pwr_cfg->data.cfg_id, 12, ".cfg", name);
        error = ffs_fread(name, &pwr_cfg->bat, PWR_BAT_CFG_SIZE);
        build_name("/pwr/bat/temp", &pwr_cfg->data.cfg_id, 13, ".cfg", name);
        error = ffs_fread(name, &pwr_cfg->temp, PWR_TEMP_CFG_SIZE);
    } else {
        // Find out which <n> and read /pwr/bat/bat<n>.cfg
        // We know that at least one battery configuration file exists
        // Pick the file that matches the bat_id measured earlier

        for (i = 1; i <= 5; i++) {
            cfg_id = i + '0';
            build_name("/pwr/bat/bat", &cfg_id, 12, ".cfg", name);
            error = ffs_fread(name, &pwr_cfg->bat, PWR_BAT_CFG_SIZE);
            // Found the right battery id??
            if ((pwr_cfg->data.bat_id >= pwr_cfg->bat.id_low) &&
                (pwr_cfg->data.bat_id <= pwr_cfg->bat.id_high)) {
                ttw(ttr(TTrInitLow, "Chose %s" NL, name));
                // Save the configuration number in the name
                pwr_ctrl->cfg_id = i;
                pwr_cfg->data.cfg_id = cfg_id;
                pwr_ctrl->flag_bat_unknown = 0;

                // Read the corresponding temperature configuration
                build_name("/pwr/bat/temp", &pwr_cfg->data.cfg_id, 13, ".cfg", name);
                error = ffs_fread(name, &pwr_cfg->temp, PWR_TEMP_CFG_SIZE);
                break;
            }
        }

        // Check if a matching battery configuration was found
        if ((pwr_cfg->data.cfg_id < '1') || (pwr_cfg->data.cfg_id > '5')) {
            pwr_cfg->data.cfg_id = '1';
            pwr_ctrl->cfg_id = 1;
            ttr(TTrWarning, "No matching battery configuration id - Defaults to %d" NL, pwr_ctrl->cfg_id);

            // Flag that battery configuration was unknown
            // Inform the MMI later when it has registered
            pwr_ctrl->flag_bat_unknown = 1;
        }
    }

    ttw(ttr(TTrInit, "pwr_read_files(%d)" NL, 0xFF));
    return RV_OK;
}


// Read calibration files only
T_RVM_RETURN pwr_read_cal_files()
{
    T_FFS_SIZE error;

    // Read /pwr/vbat.cal
    error = ffs_fread("/pwr/vbat.cal", &pwr_cfg->cal.vbat, sizeof(pwr_cfg->cal.vbat));
    if ( error < EFFS_OK )
       return RVM_INTERNAL_ERR;
    ttw(ttr(TTrInitLow, "Read /pwr/vbat.cal(%d)" NL, error));
    ttw(ttr(TTrInitLow, "pwr_cfg->cal.vbat.alfa_num=%d" NL, pwr_cfg->cal.vbat.alfa_num));
    ttw(ttr(TTrInitLow, "pwr_cfg->cal.vbat.alfa_denom=%d" NL, pwr_cfg->cal.vbat.alfa_denom));
    ttw(ttr(TTrInitLow, "pwr_cfg->cal.vbat.beta=%d" NL, pwr_cfg->cal.vbat.beta));
    return RVM_OK;

}

// This function reads the FFS pwr configuration file
// It is invoked when a charger is plugged
//                               /pwr/chg/chg<n>.cfg    MANDATORY
T_RVM_RETURN pwr_read_chg_files()
{
    T_FFS_SIZE error;
    char name[20];
    uint8 i;
    char chg_id;

    ttw(ttr(TTrInit, "pwr_read_chg_files(%d)" NL, 0));

    if (pwr_cfg->data.cforce > 0) {
        // Brute force charger configuration
        build_name("/pwr/chg/chg", &pwr_cfg->data.chg_cfg_id, 12, ".cfg", name);
        error = ffs_fread(name, &pwr_cfg->chg, PWR_CHG_CFG_SIZE);
        ttw(ttr(TTrInitLow,"error = %d" NL, error));

        // Readout /pwr/chg/chg<N>.cfg
        ttw(ttr(TTrInitLow,"chg.cfg: type: %d" NL, pwr_cfg->chg.type));
        ttw(ttr(TTrInitLow,"ichg_max: %d" NL, pwr_cfg->chg.ichg_max));
        ttw(ttr(TTrInitLow,"vchg_low: %d" NL, pwr_cfg->chg.vchg_low));
        ttw(ttr(TTrInitLow,"vchg_high: %d" NL, pwr_cfg->chg.vchg_high));
    } else {
        // Find out which <n> and read /pwr/chg/chg<n>.cfg
        // We know that at least one charger configuration file exists
        // Pick the file that matches the chg_id measured earlier

        for (i = 1; i <= 5; i++) {
            chg_id = i + '0';
            build_name("/pwr/chg/chg", &chg_id, 12, ".cfg", name);
            error = ffs_fread(name, &pwr_cfg->chg, PWR_CHG_CFG_SIZE);
            ttw(ttr(TTrInitLow,"error = %d" NL, error));

            // Readout /pwr/chg/chg<N>.cfg
            ttw(ttr(TTrInitLow,"chg.cfg: type: %d" NL, pwr_cfg->chg.type));
            ttw(ttr(TTrInitLow,"ichg_max: %d" NL, pwr_cfg->chg.ichg_max));
            ttw(ttr(TTrInitLow,"vchg_low: %d" NL, pwr_cfg->chg.vchg_low));
            ttw(ttr(TTrInitLow,"vchg_high: %d" NL, pwr_cfg->chg.vchg_high));

            // Found the right charger id??
            if ((pwr_cfg->data.chg_id > pwr_cfg->chg.vchg_low) &&
                (pwr_cfg->data.chg_id < pwr_cfg->chg.vchg_high)) {
                ttw(ttr(TTrInitLow, "Chose %s" NL, name));
                // Save the configuration number in the name
                pwr_ctrl->chg_cfg_id = i;
                pwr_cfg->data.chg_cfg_id = chg_id;
                pwr_ctrl->flag_chg_unknown = 0;

                break;
            }
        }

        // Check if a matching charger configuration was found
        if ((pwr_cfg->data.chg_cfg_id < '1') || (pwr_cfg->data.chg_cfg_id > '5')) {
            pwr_cfg->data.chg_cfg_id = '1';
            pwr_ctrl->chg_cfg_id = 1;
            ttr(TTrWarning, "No matching charger configuration id - Defaults to %d" NL, pwr_ctrl->chg_cfg_id);

            // Flag that charger configuration was unknown
            // Inform the MMI later when it has registered
            pwr_ctrl->flag_chg_unknown = 1;
        }
    }
    ttw(ttr(TTrInit, "pwr_read_chg_files(%d)" NL, 0xFF));
}

void *pwr_malloc(int size)
{
    void *addr;

    if (rvf_get_buf(pwr_ctrl->prim_id, size, &addr) == RVF_RED) {
        ttr(TTrFatal, "PWR FATAL: No Memory (%d)" NL, pwr_ctrl->state);
        return NULL;
    }

    return addr;
}

void pwr_free(void *addr)
{
    int error;

    ttw(ttr(TTrEvent, "pwr_free (%d)" NL, 0));
    if ((error = rvf_free_buf(addr)) != RV_OK) {
        ttr(TTrFatal, "PWR FATAL: pwr_free (%d)" NL, error);
    }
}


void pwr_task()
{
    void                 *request;
    struct pwr_req_s     *pwr_request;
    int    error;

    ttw(ttr(TTrEnv, "pwr_task(%d)" NL, 0));

    while (1) {
        rvf_wait(RVF_TASK_MBOX_0_EVT_MASK,0);
        request  = rvf_read_mbox(RVF_TASK_MBOX_0);
        pwr_request = (struct pwr_req_s *)request;

        if (request != NULL) {

            ttw(ttr(TTrEvent,    "Received Event(%d)" NL, pwr_request->header.msg_id));
            ttw(ttr(TTrEventLow, "src_addr_id(%d)" NL, pwr_request->header.src_addr_id));
            ttw(ttr(TTrEventLow, "dest_addr_id(%d)" NL, pwr_request->header.dest_addr_id));

            switch (pwr_request->header.msg_id) {
            case PWR_CHARGER_PLUGGED_IND:
                // Sometimes (low voltage - init) receiving a ghost charger plug although interrupts are disabled
                pwr_free(request);
                break;
            case PWR_CHARGER_UNPLUGGED_IND:
                error = process_abb_chg_unplugged_ind(pwr_request);
                break;
            case PWR_ADC_IND :
                error = process_spi_adc_indication(request);
                break;

            // Timers

            case TIMER_T1_EXPIRED:
                error = process_pwr_handle_T1_expiration(request);
                break;
            case TIMER_T2_EXPIRED:
                error = process_pwr_handle_T2_expiration(request);
                break;
            case TIMER_T3_EXPIRED:
                error = process_pwr_handle_T3_expiration(request);
                break;
            case TIMER_T4_EXPIRED:
                error = process_pwr_handle_T4_expiration(request);
                break;
            case TIMER_MOD_CYCLE_EXPIRED:
                error = process_pwr_handle_mod_cycle_expiration(request);
                break;
            case TIMER_MMI_INFO_EXPIRED:
                error = process_pwr_handle_mmi_info_expiration(request);
                break;
            default :
                {
                ttr(TTrFatal, "PWR FATAL: Unknown Event: %d" NL, pwr_request->header.msg_id);
                ttr(TTrFatal, "                   State: %d" NL, pwr_ctrl->state);
                // Exception Handling - Unknown Event
                }
            }
        } else {
            // Exception Handling - NULL pointer
            ttr(TTrFatal, "PWR FATAL: NULL pointer (%d)" NL, pwr_ctrl->state);
        }
    }
    ttw(ttr(TTrEnv, "pwr_task(%d)" NL, 0xFF));
}