/*************************************************************************** * Copyright (c) 2003-2006, Broadcom Corporation * All Rights Reserved * Confidential Property of Broadcom Corporation * * THIS SOFTWARE MAY ONLY BE USED SUBJECT TO AN EXECUTED SOFTWARE LICENSE * AGREEMENT BETWEEN THE USER AND BROADCOM. YOU HAVE NO RIGHT TO USE OR * EXPLOIT THIS MATERIAL EXCEPT SUBJECT TO THE TERMS OF SUCH AN AGREEMENT. * * $brcm_Workfile: $ * $brcm_Revision: $ * $brcm_Date: $ * * Module Description: * * Revision History: * * $brcm_Log: $ * ***************************************************************************/ #include "bapp.h" #include "bgfx.h" #include "bstd.h" #include "led.h" #include "bapp_palette.h" BDBG_MODULE(bapp); /* Register software module with debug interface */ static const unsigned short s_volume[] = { 4096, 3254, 2584, 2053, 1631, 1295, 1029, 817, 649, 516, 410, 325, 258, 205, 163, 130, 103, 82, 65, 52, 41, 33, 26, 21, 16, 13, 10, 8, 6, 5, 4, 3 }; const int s_volume_num = sizeof(s_volume)/sizeof(unsigned short); /** Summary: IO structure passed to bgfx when initialized **/ static bgfx_io_t s_io = { bin_read, bin_tell, bin_set }; #define BAPP_VCHIP_TIMEOUT (7000) /* Milliseconds */ #define BAPP_RATINGS_EVAL_DELAY (500) /* Milliseconds */ #define DEFAULT_VOLUME 0 #define DEFAULT_RFM_VOLUME 78 #define BAPP_DIAG_TIMEOUT 3 /* Seconds */ #define BAPP_VCHIP_MSG BDBG_WRN #define TIME_APP_DRAWING static bgfx_font_t s_font[eFONT_SIZE_MAX]; static void bapp_eval_state(bapp_t *p_app,bapp_state_t new_state); #if !defined(RAM_BUILD) static int bapp_stop_decode(bapp_t *p_app); #endif static int bapp_channel_hunt(bapp_t *p_app); static void bapp_standby(bapp_t *p_app, bool power); static void bapp_new_screen(bapp_t *p_app); extern unsigned int g_FrancophilB_22_mono_size; extern const unsigned char g_FrancophilB_22_mono[]; extern unsigned int g_FrancophilB_28_mono_size; extern const unsigned char g_FrancophilB_28_mono[]; /* * Global app reference used by a couple callbacks to reference the application. */ bapp_t *s_p_app = NULL; static bscreen_t g_screens[] = { /* title_text_id desc_text_id help_text_id top_banner_height num_buttons idle_timeout p_button_array (* draw)() (* handle_event)() local_state */ { eTEXT_MAX, eTEXT_MAX, eTEXT_MAX, 0, 0, 0, 0, BANNER_SMALL_TIMEOUT, eSCREEN_MAX, 0, bscreen_banner_draw, screen_banner_event, (uint32_t)&g_banner_state}, { eTEXT_MAX, eTEXT_MAX, eTEXT_MAX, 0, 0, 0, 0, 0, eSCREEN_MAX, 0, bscreen_screensaver_draw, screen_screensaver_event, (uint32_t)&g_screensaver_state}, { eTEXT_MAX, eTEXT_MAX, eTEXT_MAX, 0, 0, 0, 0, 0, eSCREEN_MAX, 0, bscreen_power_off_draw, screen_power_off_event, 0}, { eTEXT_MAX, eTEXT_MAX, eTEXT_MAX, 0, 0, 0, 0, SCREEN_DEFAULT_TIMEOUT, eSCREEN_MAX, 0, bscreen_diag_menu_draw, bscreen_diag_menu_event, 0}, { eTEXT_MAX, eTEXT_MAX, eTEXT_MAX, 0, 0, 0, 0, SCREEN_DEFAULT_TIMEOUT, eSCREEN_MAX, 0, bscreen_sys_info_draw, bscreen_sys_info_event, 0}, { eTEXT_MAX, eTEXT_MAX, eTEXT_MAX, 0, 0, 0, 0, SCREEN_DEFAULT_TIMEOUT, eSCREEN_MAX, 0, bscreen_dta_status_draw, bscreen_dta_status_event, 0}, { eTEXT_MAX, eTEXT_MAX, eTEXT_MAX, 0, 0, 0, 0, SCREEN_DEFAULT_TIMEOUT, eSCREEN_MAX, 0, bscreen_mso_info_draw, bscreen_mso_info_event, 0}, { eTEXT_MAX, eTEXT_MAX, eTEXT_MAX, 0, 0, 0, 0, SCREEN_DEFAULT_TIMEOUT, eSCREEN_MAX, 0, bscreen_tuner_status_draw, bscreen_tuner_status_event, 0}, { eTEXT_MAX, eTEXT_MAX, eTEXT_MAX, 0, 0, 0, 0, SCREEN_DEFAULT_TIMEOUT, eSCREEN_MAX, 0, bscreen_ch_status_draw, bscreen_ch_status_event, 0}, { eTEXT_MAX, eTEXT_MAX, eTEXT_MAX, 0, 0, 0, 0, SCREEN_DEFAULT_TIMEOUT, eSCREEN_MAX, 0, bscreen_ch_map_status_draw, bscreen_ch_map_status_event, 0}, { eTEXT_MAX, eTEXT_MAX, eTEXT_MAX, 0, 0, 0, 0, SCREEN_DEFAULT_TIMEOUT, eSCREEN_MAX, 0, bscreen_msg_status_draw, bscreen_msg_status_event, 0}, { eTEXT_MAX, eTEXT_MAX, eTEXT_MAX, 0, 0, 0, 0, SCREEN_DEFAULT_TIMEOUT, eSCREEN_MAX, 0, bscreen_code_objects_draw, bscreen_code_objects_event, 0}, { eTEXT_MAX, eTEXT_MAX, eTEXT_MAX, 0, 0, 0, 0, SCREEN_DEFAULT_TIMEOUT, eSCREEN_MAX, 0, bscreen_code_download_draw, bscreen_code_download_event, 0}, { eTEXT_MAX, eTEXT_MAX, eTEXT_MAX, 0, 0, 0, 0, SCREEN_DEFAULT_TIMEOUT, eSCREEN_MAX, 0, bscreen_decoder_status_draw, bscreen_decoder_status_event, 0}, /* { eTEXT_MAX, eTEXT_MAX, eTEXT_MAX, 0, 0, 0, 0, SCREEN_DEFAULT_TIMEOUT, eSCREEN_MAX, 0, bscreen_hh_draw, bscreen_hh_event, 0},*/ { eTEXT_MAX, eTEXT_MAX, eTEXT_MAX, 0, 0, 0, 0, SCREEN_DEFAULT_TIMEOUT, eSCREEN_MAX, 0, bscreen_status_draw, bscreen_status_event, 0}, }; const unsigned char g_screens_num = sizeof(g_screens)/sizeof(g_screens[0]); void bl_write(void) { } #ifdef BDBG_DEBUG_BUILD static const char *s_chm_evt_str[] = { "CHM_EVT_CANCEL", "CHM_EVT_DONE", "CHM_EVT_REDRAW", "CHM_EVT_PROGRESS", "CHM_EVT_SIGNAL", "CHM_EVT_DOWNLOAD", "CHM_EVT_EAS", "CHM_EVT_CA", "CHM_EVT_NETWORK", "CHM_EVT_STATUS", "CHM_EVT_AV_MUTE", "CHM_EVT_ACTIVATION", "CHM_EVT_CONFIGURATION", "CHM_EVT_VCT_ID", "CHM_EVT_LOCATION", "CHM_EVT_TIMEZONE", "CHM_EVT_PHONENUMBER", "CHM_EVT_RESET", "CHM_EVT_SAVE", "CHM_EVT_TIME", }; #endif /* Summary: Set the LED mode based on the state and settings. */ void bapp_led_set_mode(bapp_t *p_app) { enum led_mode_t mode = led_off_mode; if (!p_app->power) { mode = led_off_mode; } else { switch (p_app->state) { case eAPP_STATE_HUNT: mode = led_hunt_mode; break; case eAPP_STATE_PENDING: mode = led_pending_init_mode; break; case eAPP_STATE_NORMAL: mode = led_on_mode; break; case eAPP_STATE_DOWNLOAD: mode = led_download_mode; break; case eAPP_STATE_FACTORY: case eAPP_STATE_DEFAULT: case eAPP_STATE_DONE: break; } } if (mode == p_app->led_mode) { return; } p_app->led_mode = mode; led_set_mode(mode); } /* * Flush cache */ void bapp_flush_screen(bapp_t *p_app) { BDBG_WRN(("%s:%d-%d\n",__FUNCTION__,__LINE__,p_app->nexus.surface_idx)); NEXUS_Surface_Flush(p_app->nexus.surface[p_app->nexus.surface_idx]); NEXUS_Display_SetGraphicsFramebuffer(p_app->nexus.display, p_app->nexus.surface[p_app->nexus.surface_idx]); p_app->nexus.surface_idx++; if (p_app->nexus.surface_idx >= BAPP_NUM_SURFACES) { p_app->nexus.surface_idx = 0; } p_app->surf.surface.buf = p_app->nexus.mem[p_app->nexus.surface_idx].buffer; } /* Summary: Set volume level Description: Set Volume level (range 0(full) - s_volume_num) */ void bapp_set_audio_volume(bapp_t *p_app, uint8_t level) { uint32_t new_vol = level; uint32_t cur_vol = p_app->audio_vol; uint32_t vol; /* possibly cancel mute */ if (level < (s_volume_num - 1)) { bapp_audio_mute(p_app, 0); } BDBG_WRN(("%s audio_vol = %d, new_vol = %d, is_muted = %d...\n",__FUNCTION__,p_app->audio_vol,new_vol,p_app->is_muted)); if ((level >= s_volume_num) || (new_vol == cur_vol)) return; if (new_vol > cur_vol) { for (p_app->audio_vol = cur_vol; p_app->audio_vol < new_vol; ++p_app->audio_vol) { vol = s_volume[p_app->audio_vol]; /* JPF TODO Set the volume here */ bapp_task_sleep(1); } } else { for (p_app->audio_vol = cur_vol; p_app->audio_vol > new_vol; --p_app->audio_vol) { vol = s_volume[p_app->audio_vol]; /* JPF TODO Set the volume here */ bapp_task_sleep(1); } } p_app->audio_vol = level; vol = s_volume[p_app->audio_vol]; /* JPF TODO Set the volume here */ } /* Summary: Mute audio Description: Mute audio when enable is non-zero. */ void bapp_audio_mute(bapp_t *p_app, int mute) { int vol,idx; if ((mute && p_app->is_muted) || (!mute && !p_app->is_muted) || p_app->settings.av_mute) return; BDBG_WRN(("%s mute = %d, audo_vol = %d, is_muted = %d...\n",__FUNCTION__,mute,p_app->audio_vol,p_app->is_muted)); if (mute) { for (idx = p_app->audio_vol; idx < s_volume_num; ++idx) { vol = s_volume[idx]; /* JPF TODO Set the volume here */ bapp_task_sleep(1); } p_app->is_muted = true; } else { for (idx = (s_volume_num-1); idx >= p_app->audio_vol; --idx) { vol = s_volume[idx]; /* JPF TODO Set the volume here */ bapp_task_sleep(1); } p_app->is_muted = false; } } /* Summary: Mute both audio and video. Description: Mute both audio and video when enable is non-zero. */ void bapp_av_mute(bapp_t *p_app, int mute) { #if 0 /* JPF TODO MUTE DISPLAY AND AUDIO */ bdecode_config cfg; bapp_audio_mute(p_app,mute); bdecode_get_config(p_app->decode,&cfg); if (mute) cfg.mute = 1; else cfg.mute = 0; bdecode_set_config(p_app->decode,&cfg); if (mute) { bapp_sync(p_app); bgfx_fill_rect(&p_app->surf,0,0,eWIDTH,eHEIGHT,eCOLOR_CLEAR); bapp_flush_screen(p_app); } p_app->settings.av_mute = mute; p_app->settings_dirty = true; #endif } /* Summary: Configure the video output mode. Description: Configure the video output mode. */ void bapp_output_mode(bapp_t *p_app, int widescreen) { #if 0 /* JPF TODO SET OUTPUT MODE- extend to HD */ bdecode_config cfg; bdecode_get_config(p_app->decode,&cfg); if (widescreen) { BDBG_WRN(("Widescreen (%d)\n", widescreen)); cfg.widescreen = widescreen; } else { BDBG_WRN(("Full Screen...\n")); cfg.widescreen = 0; } bdecode_set_config(p_app->decode,&cfg); #endif } /* Summary: Apply the current settings. Description: Apply the current settings. Restart decode. */ static void bapp_apply_settings(bapp_t *p_app) { p_app->audio_vol = p_app->settings.audio_vol; bapp_output_mode(p_app, 0); bapp_set_audio_volume(p_app, p_app->audio_vol); } /* Summary: Reset the current user settings Description: Reset the current user settings. Note this does not change them in flash. */ void bapp_reset_settings(bapp_t *p_app) { memset(&p_app->settings, 0, sizeof(p_app->settings)); ch_map_init(&(p_app->settings.ch_map),p_app->ch_map_mutex); p_app->settings.network_PID = CONFIG_NETWORK_PID; p_app->settings.VCT_ID = CONFIG_DEF_VCT_ID; p_app->settings.EMM_PID = CONFIG_DEF_EMM_PID; /* this will make sure volume is reset to default from UI */ p_app->audio_vol = p_app->settings.audio_vol; } /* Summary: Calculate the ch_map storage size in bytes */ typedef struct settings_hdr_t { uint32_t settings_size; uint32_t st_cnt; uint32_t vch_cnt; uint32_t mms_cnt; uint32_t freq_cnt; uint32_t cur_ch; }settings_hdr_t; static void bapp_calc_settings_hdr(bapp_t *p_app, settings_hdr_t *p_hdr) { p_hdr->freq_cnt = p_app->settings.ch_map.freq_map.num_freq; p_hdr->mms_cnt = p_app->settings.ch_map.mms_map.num_mms; p_hdr->st_cnt = p_app->settings.ch_map.st_map.num_st; p_hdr->vch_cnt = p_app->settings.ch_map.vch_map.num_vch; p_hdr->cur_ch = p_app->settings.ch_map.cur_ch; /* also store cur_ch in the header */ p_hdr->settings_size = sizeof(settings_hdr_t); /* Size header */ p_hdr->settings_size += ((uint32_t)&(p_app->settings.ch_map) - (uint32_t)&(p_app->settings)); /* Settings minus ch_map */ p_hdr->settings_size += (p_hdr->mms_cnt * sizeof(mms_t)); /* mms records */ p_hdr->settings_size += (p_hdr->freq_cnt * sizeof(freq_t)); /* freq records */ p_hdr->settings_size += (p_hdr->st_cnt * sizeof(st_t)); /* st records */ p_hdr->settings_size += (p_hdr->vch_cnt * sizeof(vch_t)); /* vch records */ } /* Summary: Load current settings from NVM */ static void bapp_load_settings(bapp_t *p_app) { settings_hdr_t settings_hdr; uint8_t *p_flash_data = NULL; uint32_t flash_data_length; uint32_t offset = 0; uint32_t size = 0; if (fstore_get(&p_app->flash_storage, eFS_APP_DATA, (void **)&(p_flash_data), &flash_data_length) == b_ok) { memcpy(&settings_hdr,p_flash_data,sizeof(settings_hdr_t)); if (flash_data_length == settings_hdr.settings_size) { /* flash data valid - use it! */ BDBG_WRN(("Flash valid app data length %d bytes restore settings\n", flash_data_length)); ch_map_init(&(p_app->settings.ch_map),p_app->ch_map_mutex); ch_map_reset(&(p_app->settings.ch_map)); if (bapp_task_acquire_mutex(p_app->settings.ch_map.p_mutex,45) == b_ok) { p_app->settings.ch_map.freq_map.num_freq = (uint8_t)settings_hdr.freq_cnt; p_app->settings.ch_map.mms_map.num_mms = (uint8_t)settings_hdr.mms_cnt; p_app->settings.ch_map.st_map.num_st = (uint16_t)settings_hdr.st_cnt; p_app->settings.ch_map.vch_map.num_vch = (uint16_t)settings_hdr.vch_cnt; p_app->settings.ch_map.cur_ch = (uint16_t)settings_hdr.cur_ch; offset = sizeof(settings_hdr_t); size = ((uint32_t)&(p_app->settings.ch_map) - (uint32_t)&(p_app->settings)); memcpy(&p_app->settings,&p_flash_data[offset],size); /* Copy settings minus the ch_map */ offset += size; size = sizeof(mms_t) * p_app->settings.ch_map.mms_map.num_mms; memcpy(p_app->settings.ch_map.mms_map.mms,&p_flash_data[offset],size); /* Copy settings mms portion of the ch_map */ offset += size; size = sizeof(freq_t) * p_app->settings.ch_map.freq_map.num_freq; memcpy(p_app->settings.ch_map.freq_map.freq,&p_flash_data[offset],size); /* Copy settings freq portion of the ch_map */ offset += size; size = sizeof(st_t) * p_app->settings.ch_map.st_map.num_st; memcpy(p_app->settings.ch_map.st_map.st,&p_flash_data[offset],size); /* Copy settings st portion of the ch_map */ offset += size; size = sizeof(vch_t) * p_app->settings.ch_map.vch_map.num_vch; memcpy(p_app->settings.ch_map.vch_map.vch,&p_flash_data[offset],size); /* Copy settings vch portion of the ch_map */ bapp_task_release_mutex(p_app->settings.ch_map.p_mutex); p_app->settings_dirty = false; ch_output(bapp_cur_ch_map(p_app)); } else { BDBG_WRN(("%s:%d mutex timeout\n", __FUNCTION__,__LINE__)); } return; } } /* flash contains invalid saved data */ BDBG_WRN(("Flash app data length %d not equal to header length %d, reset.\n", flash_data_length, settings_hdr.settings_size)); bapp_reset_settings(p_app); p_app->audio_vol = p_app->settings.audio_vol; /* update flash with new settings */ bapp_save_settings(p_app); } /* Summary: Save current settings to NVM Description: Save current settings to NVM */ void bapp_save_settings(bapp_t *p_app) { settings_hdr_t settings_hdr; uint8_t *p_flash_data = NULL; uint32_t offset = 0; uint32_t size = 0; /* update unsaved settings */ p_app->settings.audio_vol = p_app->audio_vol; bapp_calc_settings_hdr(p_app,&settings_hdr); p_flash_data = bapp_util_malloc(settings_hdr.settings_size); if (!p_flash_data) { BDBG_WRN(("Could not find temporary memory to save settings to flash\n", settings_hdr.settings_size)); return; } BDBG_WRN(("Flash data length %d bytes, save settings\n", settings_hdr.settings_size)); memcpy(p_flash_data,&settings_hdr,sizeof(settings_hdr_t)); if (bapp_task_acquire_mutex(p_app->settings.ch_map.p_mutex,45) == b_ok) { offset = sizeof(settings_hdr_t); size = ((uint32_t)&(p_app->settings.ch_map) - (uint32_t)&(p_app->settings)); memcpy(&p_flash_data[offset],&p_app->settings,size); /* Copy settings minus the ch_map */ offset += size; size = sizeof(mms_t) * p_app->settings.ch_map.mms_map.num_mms; memcpy(&p_flash_data[offset],p_app->settings.ch_map.mms_map.mms,size); /* Copy settings mms portion of the ch_map */ offset += size; size = sizeof(freq_t) * p_app->settings.ch_map.freq_map.num_freq; memcpy(&p_flash_data[offset],p_app->settings.ch_map.freq_map.freq,size); /* Copy settings freq portion of the ch_map */ offset += size; size = sizeof(st_t) * p_app->settings.ch_map.st_map.num_st; memcpy(&p_flash_data[offset],p_app->settings.ch_map.st_map.st,size); /* Copy settings st portion of the ch_map */ offset += size; size = sizeof(vch_t) * p_app->settings.ch_map.vch_map.num_vch; memcpy(&p_flash_data[offset],p_app->settings.ch_map.vch_map.vch,size); /* Copy settings vch portion of the ch_map */ bapp_task_release_mutex(p_app->settings.ch_map.p_mutex); } else { BDBG_WRN(("%s:%d mutex timeout\n", __FUNCTION__,__LINE__)); return; } p_app->settings_dirty = false; if (fstore_update(&p_app->flash_storage, eFS_APP_DATA, p_flash_data, settings_hdr.settings_size) != b_ok) { BDBG_WRN(("fstore_update failed\n")); } else { BDBG_WRN(("fstore_update success\n")); } } /* Summary: Power down audio DAC and power off */ void bapp_do_poweroff(bapp_t *p_app) { /* JPF TODO Power OFF */ } /* Summary: Initialize the main app structure. Description: Do necessary configuration of main app structure. Assumes p_app is allocated and p_surf and palette are initialized. */ void bapp_init(bapp_t *p_app) { bapp_lang_t e_lang; bresult rc; unsigned int *palette; /* [out] address of palette */ int width; /* [out] width of the OSD surface */ int height; /* [out] height of the OSD surface */ int pitch; /* [out] pitch of the OSD surface */ BDBG_MSG(("%s:%d\n",__FUNCTION__,__LINE__)); memset(p_app,0,sizeof(bapp_t)); s_p_app = p_app; p_app->last_key_down = (bIR_codes_t)0xFFF; p_app->last_screen_id = eSCREEN_MAX; /* Initialize Nexus */ bapp_nexus_open(&(p_app->nexus), eTUNE_TYPE_QAM); /* initialize bgfx */ bgfx_init(&s_io,NULL); rc = bgfx_create(&(p_app->surf), p_app->nexus.createSettings[p_app->nexus.surface_idx].width, p_app->nexus.createSettings[p_app->nexus.surface_idx].height, (uint8_t*)p_app->nexus.mem[p_app->nexus.surface_idx].buffer, p_app->nexus.mem[p_app->nexus.surface_idx].pitch, (bgfx_palette_t*)p_app->nexus.mem[p_app->nexus.surface_idx].palette, BGFX_SURF_PRIMARY | BGFX_SURF_BPP(bapp_get_bpp())); BDBG_ASSERT(rc == 0); /* Open IR */ bapp_remote_open(&(p_app->remote)); BDBG_ASSERT(p_app->remote); GETTIMEOFDAY(&p_app->timeout_tv); smessage_init(NULL); bapp_task_create_queue(&p_app->msg_queue, p_app->msg_event, MAX_MSG_EVENT); BDBG_ASSERT(p_app->msg_queue); rc = bapp_task_create_mutex(&p_app->ch_map_mutex); BDBG_ASSERT(rc == b_ok); ch_map_init(&(p_app->settings.ch_map),p_app->ch_map_mutex); /* should after bgraphics_open, since bgraphics_open will overwrite some BVN TOP registers, e.g. AMOL */ chm_init(&p_app->chm,p_app); p_app->yield_ms = APP_DEFAULT_YIELD; p_app->lang = eLANG_ENGLISH; p_app->width = p_app->nexus.createSettings[p_app->nexus.surface_idx].width; p_app->height = p_app->nexus.createSettings[p_app->nexus.surface_idx].height; BDBG_MSG(("%s:%d - Width = %d, Height = %d, Pitch = %d.\n",__FUNCTION__,__LINE__,width,height,pitch)); BDBG_MSG(("%s:%d - Initialize screens.\n",__FUNCTION__,__LINE__)); /* Default to NULL (empty) screen (TODO: goto factory test then setup wizard.) */ p_app->screen_id = eSCREEN_BANNER; p_app->p_screens = g_screens; p_app->num_screens = g_screens_num; BDBG_MSG(("%s:%d - Load fonts.\n",__FUNCTION__,__LINE__)); memset(&s_font[eFONT_SIZE_SMALL],0,sizeof(bgfx_font_t) * 3); p_app->br.cnt = 0; p_app->br.data = (unsigned char*)g_FrancophilB_22_mono; p_app->br.size = g_FrancophilB_22_mono_size; BDBG_ASSERT(bgfx_load_font(&(s_font[eFONT_SIZE_SMALL]),&p_app->br) == 0); p_app->br.cnt = 0; p_app->br.data = (unsigned char*)g_FrancophilB_28_mono; p_app->br.size = g_FrancophilB_28_mono_size; BDBG_ASSERT(bgfx_load_font(&(s_font[eFONT_SIZE_MED]),&p_app->br) == 0); for (e_lang = eLANG_ENGLISH; e_lang < eLANG_MAX; ++e_lang) { p_app->p_font[e_lang][eFONT_SIZE_SMALL] = &s_font[eFONT_SIZE_SMALL]; p_app->p_font[e_lang][eFONT_SIZE_MED] = &s_font[eFONT_SIZE_MED]; } BDBG_ASSERT(fstore_init(&(p_app->flash_storage)) == b_ok); bapp_load_settings(p_app); if (p_app->settings.activated) { if (((int)p_app->settings.timeout_cnt) >= 2) { p_app->settings.timeout_cnt -= 2; } else { p_app->settings.activated = false; p_app->settings_dirty = true; } } #ifdef CONFIG_DTA_CABLE_TESTING { bapp_task_mutex_t *p_mutex = p_app->settings.ch_map.p_mutex; extern const ch_map_t s_ch_map; memcpy(&p_app->settings.ch_map,&s_ch_map, sizeof(s_ch_map)); p_app->settings.ch_map.p_mutex = p_mutex; p_app->settings.activated = true; } #endif bapp_apply_settings(p_app); BDBG_WRN(("%s:%d - av_mute = %d\n",__FUNCTION__,__LINE__,p_app->settings.av_mute)); BDBG_WRN(("%s:%d - prev_state = %d\n",__FUNCTION__,__LINE__,p_app->settings.state)); bapp_av_mute(p_app,0); bapp_set_current_screen(p_app, eSCREEN_BANNER, eSCREEN_MAX); bapp_new_screen(p_app); p_app->power = 1; /* Always enter pending state on hard power up. */ #ifdef CONFIG_DTA_CABLE_TESTING bapp_eval_state(p_app,eAPP_STATE_NORMAL); #else if (p_app->settings.psi_scan) bapp_eval_state(p_app,eAPP_STATE_NORMAL); else bapp_eval_state(p_app,eAPP_STATE_HUNT); #endif #if 0 #ifdef BDBG_DEBUG_BUILD BDBG_SetModuleLevel("bscreen",BDBG_eMsg); #endif BDBG_SetModuleLevel("xpt_mesg",BDBG_eMsg); BDBG_SetModuleLevel("nexus_message",BDBG_eMsg); BDBG_SetModuleLevel("BXVD",BDBG_eMsg); BDBG_SetModuleLevel("nexus_video_decoder",BDBG_eMsg); BDBG_SetModuleLevel("nexus_audio_decoder",BDBG_eTrace); BDBG_SetModuleLevel("nexus_demux",BDBG_eMsg); BDBG_SetModuleLevel("nexus_audio_dac",BDBG_eTrace); BDBG_SetModuleLevel("rap_output",BDBG_eTrace); #endif } /* Summary: Main app event handler. Description: Main application event handler. */ void bapp_handle_event(bapp_t *p_app, bscreen_event_t *p_event) { if (p_event && (p_event->type == eS_EVENT_IR) && (p_event->id >= eIR_CHMAP)) { BDBG_WRN(("%s:%d - event_id = 0x%08x\n",__FUNCTION__,__LINE__,p_event->id)); switch (p_event->id) { case eIR_CHMAP: /* Output channel map on UART */ ch_output(bapp_cur_ch_map(p_app)); break; case eIR_SCAN: /* Perform channel scan */ p_app->state = eAPP_STATE_NORMAL; if (p_app->settings.psi_scan) { p_app->settings.activated = 1; p_app->settings_dirty = true; } bapp_eval_state(p_app,eAPP_STATE_HUNT); if (p_app->settings.psi_scan) { bapp_eval_state(p_app,eAPP_STATE_NORMAL); } break; case eIR_SETUP: /* Perform default setup */ p_app->state = eAPP_STATE_NORMAL; bapp_eval_state(p_app,eAPP_STATE_DOWNLOAD); break; case eIR_EXIT: /* Exit to banner menu */ bapp_set_current_screen(p_app, eSCREEN_BANNER, eSCREEN_MAX); break; default: if ((p_event->id & eIR_SETCH) == eIR_SETCH) { /* TODO Tune to channel number */ bapp_set_current_screen(p_app, eSCREEN_BANNER, eSCREEN_MAX); } break; } } } /* Summary: Main app idle time handler. Description: Main application idle time handler. */ void bapp_idle(bapp_t *p_app) { struct timeval end_tv; unsigned int delta_hrs; static chm_event_t evt; GETTIMEOFDAY(&end_tv); delta_hrs = (end_tv.tv_sec - p_app->timeout_tv.tv_sec)/(60 * 60); if ((delta_hrs > 0) && (((int)p_app->settings.timeout_cnt) > 0)) { p_app->settings.timeout_cnt--; BDBG_WRN(("%s decriment timeout counter %d\n",__FUNCTION__,p_app->settings.timeout_cnt)); p_app->timeout_tv = end_tv; } if ((p_app->state == eAPP_STATE_NORMAL) && p_app->settings.activated && !p_app->settings.psi_scan) { if (((int)p_app->settings.timeout_cnt) <= 0) { evt.id = 0; evt.type = eCHM_EVT_ACTIVATION; bapp_task_post_event(p_app->msg_queue,(bapp_task_event_t)&evt); } } if (p_app->check_poweron) { if (p_app->poweron_ms < bapp_task_getticks()) { p_app->check_poweron = false; } } if ((p_app->state == eAPP_STATE_NORMAL) && p_app->eas_timeout && (p_app->eas_timeout < (unsigned int)end_tv.tv_sec)) { p_app->eas_timeout = 0; if (p_app->eas_text) { p_app->eas_text = false; /* clear eas text scrolling screen */ bapp_sync(p_app); screen_null_draw(p_app, NULL); bapp_flush_screen(p_app); } bapp_tune_prev(p_app); bapp_set_current_screen(p_app, eSCREEN_BANNER, eSCREEN_MAX); } #ifndef OLD_INFO_KEY_PROCESSING if (p_app->last_key_down == eIR_INFO) { struct timeval cur_tv; BDBG_WRN(("%s: p_app->last_key_down == eIR_INFO \n",__FUNCTION__)); GETTIMEOFDAY(&cur_tv); if ((p_app->last_key_tv.tv_sec + BAPP_DIAG_TIMEOUT) <= cur_tv.tv_sec) { BDBG_WRN(("%s: DIAG_TIMEOUT \n",__FUNCTION__)); bapp_set_current_screen(p_app, eSCREEN_DIAG_MENU, eSCREEN_MAX); bapp_new_screen(p_app); } } #endif } /* Summary: Wait for vsync. Description: Block waiting for vsync so drawing can by syncronized with screen refresh. */ void bapp_sync(bapp_t *p_app) { /* do nothing */ } /* Summary: Enable closed captioning. Description: Enable closed captioning and send clear characters. */ void bapp_enable_cc(bapp_t *p_app, int enable) { #if 0 /* JPF TODO */ bdecode_config cfg; bdecode_get_config(p_app->decode,&cfg); if (cfg.cc_enabled != enable) { cfg.cc_enabled = (enable == 0) ? 0 : 1; bdecode_set_config(p_app->decode,&cfg); } #endif } /* Summary: Handle new screen. Description: Block waiting for vsync so drawing can by syncronized with screen refresh. */ static void bapp_screen_redraw(bapp_t *p_app) { bscreen_event_t screen_event; screen_event.type = eS_EVENT_REDRAW; screen_event.id = 0; BDBG_MSG(("%s:%d - sending setup event to %d.\n",__FUNCTION__,__LINE__,p_app->screen_id)); if (p_app->p_screens[p_app->screen_id].handle_event(p_app,(void*)&(p_app->p_screens[p_app->screen_id]),&screen_event)) { p_app->last_screen_id = -1; bapp_new_screen(p_app); } } /* Summary: Handle new screen. Description: Block waiting for vsync so drawing can by syncronized with screen refresh. */ static void bapp_new_screen(bapp_t *p_app) { bscreen_event_t screen_event; #ifdef TIME_APP_DRAWING struct timeval start_tv,end_tv,result_tv; unsigned int dt; #endif screen_event.type = 0; if (p_app->last_screen_id != p_app->screen_id) { BDBG_WRN(("%s:%d - sending setup event to %d.\n",__FUNCTION__,__LINE__,p_app->screen_id)); screen_event.type = eS_EVENT_SETUP; screen_event.id = 0; p_app->p_screens[p_app->screen_id].handle_event(p_app,(void*)&(p_app->p_screens[p_app->screen_id]),&screen_event); } p_app->last_screen_id = p_app->screen_id; #ifdef TIME_APP_DRAWING GETTIMEOFDAY(&start_tv); #endif bapp_sync(p_app); p_app->p_screens[p_app->screen_id].draw(p_app,(void*)&(p_app->p_screens[p_app->screen_id])); bapp_flush_screen(p_app); #ifdef TIME_APP_DRAWING GETTIMEOFDAY(&end_tv); timeval_subtract(&result_tv,&end_tv,&start_tv); dt = result_tv.tv_sec * 1000 + result_tv.tv_usec/1000; BDBG_WRN(("Drawing screen %d took %d ms\n",p_app->screen_id,dt)); #endif /* send a setup done event to start any post drawing setup */ if (screen_event.type == eS_EVENT_SETUP) { screen_event.type = eS_EVENT_SETUP_DONE; screen_event.id = 0; if (p_app->p_screens[p_app->screen_id].handle_event(p_app,(void*)&(p_app->p_screens[p_app->screen_id]),&screen_event)) { p_app->last_screen_id = p_app->screen_id; screen_event.type = eS_EVENT_SETUP; screen_event.id = 0; p_app->p_screens[p_app->screen_id].handle_event(p_app,(void*)&(p_app->p_screens[p_app->screen_id]),&screen_event); #ifdef TIME_APP_DRAWING GETTIMEOFDAY(&start_tv); #endif bapp_sync(p_app); p_app->p_screens[p_app->screen_id].draw(p_app,(void*)&(p_app->p_screens[p_app->screen_id])); bapp_flush_screen(p_app); #ifdef TIME_APP_DRAWING GETTIMEOFDAY(&end_tv); timeval_subtract(&result_tv,&end_tv,&start_tv); dt = result_tv.tv_sec * 1000 + result_tv.tv_usec/1000; BDBG_WRN(("Drawing took %d milliseconds\n",dt)); #endif } } } /* Summary: sets the current screen for display Description: sets the current screen for display. if id == eSCREEN_MAX then simply backup a screen if save_last == eSCREEN_MAX, also saves last screen id. */ void bapp_set_current_screen(bapp_t *p_app, bapp_screen_id_t id, bapp_screen_id_t save_last) { bscreen_t *p_screen = &(p_app->p_screens[p_app->screen_id]); bscreen_t *p_screen_new = &(p_app->p_screens[id]); #ifdef BDBG_DEBUG_BUILD unsigned int cur_screen = p_app->screen_id; #endif BDBG_WRN(("Attempt to change screen from %d to %d\n",cur_screen, id)); if (id == p_app->screen_id) return; if (id == eSCREEN_MAX) { bapp_goto_last_screen(p_app); } else { if (save_last == eSCREEN_MAX) p_screen_new->last_screen_id = p_app->screen_id; else { p_screen_new->last_screen_id = save_last; p_screen->button_selection = 0; /* if we do not save the last screen, clear out focus history */ } if (id < eSCREEN_MAX) p_app->screen_id = id; } if (p_app->screen_id == eSCREEN_MAX) { p_app->screen_id = eSCREEN_BANNER; /* Default to channel change screen */ } BDBG_WRN(("Changing screen from %d to %d\n",cur_screen, p_app->screen_id)); } /* Summary: sets the current screen for display to be the last screen Description: sets the current screen for display to be the last screen */ void bapp_goto_last_screen(bapp_t *p_app) { bscreen_t *p_screen = &(p_app->p_screens[p_app->screen_id]); p_app->screen_id = p_screen->last_screen_id; if (p_app->screen_id == eSCREEN_MAX) { p_app->screen_id = eSCREEN_BANNER; /* Default to channel change screen */ } } /* Summary: Enter/exit standby */ static void bapp_standby(bapp_t *p_app, bool power) { if (!power && p_app->check_poweron) { return; } if (power) { /* turning power on */ BDBG_ERR(("######## Turn ON ########\n")); bapp_set_palette(p_app, ePALETTE_DEFAULT); /* restore settings */ p_app->audio_vol = p_app->settings.audio_vol; /* restore last volume level */ p_app->audio_mute = false; /* ensure audio mute user setting is off */ bapp_av_mute(p_app,0); bapp_audio_mute(p_app,0); bapp_set_current_screen(p_app, eSCREEN_BANNER, eSCREEN_MAX); bapp_new_screen(p_app); if (p_app->state == eAPP_STATE_NORMAL) { bapp_tune(p_app); } } else { /* turning power off */ #ifdef BDBG_DEBUG_BUILD uint32_t current_tick = bapp_task_getticks(); #endif bapp_av_mute(p_app,1); BDBG_ERR(("######## Turn OFF @ 0x%08x ########\n",TICKS_TO_MS(current_tick))); bapp_set_current_screen(p_app, eSCREEN_POWER_OFF, eSCREEN_MAX); bapp_new_screen(p_app); p_app->settings.state = p_app->state; p_app->settings_dirty = true; /* save settings if saved channel number or saved volume level is incorrect note that these settings are saved here because the change often during normal stb operation and there is no need to maintain this information except between power cycles. these settings are also duplicated in both the settings struct and the p_app struct.*/ if ((p_app->settings.audio_vol != p_app->audio_vol) || p_app->settings_dirty) { p_app->settings.audio_vol = p_app->audio_vol; bapp_save_settings(p_app); } BDBG_WRN(("######## NOTIFY POWERCTRL ########")); } p_app->power = power; } /* Summary: Handle diag events. */ #ifdef OLD_INFO_KEY_PROCESSING static bool bapp_enter_diag(bapp_t *p_app,bIR_codes_t code) { bool result = false; switch (code) { case eIR_INFO: bapp_set_current_screen(p_app, eSCREEN_DIAG_MENU, eSCREEN_MAX); bapp_new_screen(p_app); result = true; break; default: break; } return result; } #endif /* Summary: Handle user events. */ static void bapp_handle_user_event(bapp_t *p_app, bapp_event_t *p_event ) { bscreen_event_t screen_event; bool key_up = (p_event->id & 0x40000000) ? true : false; bIR_codes_t code = (bIR_codes_t)(p_event->id & ~0x40000000); #ifdef OLD_INFO_KEY_PROCESSING struct timeval cur_tv; #endif BDBG_WRN(("%s:%d 0x%02x(%s)\n",__FUNCTION__,__LINE__,code,(key_up) ? "up" : "down")); switch (code) { case eIR_POWER: if (key_up && !p_app->check_poweron) bapp_standby(p_app, 1/*!p_app->power*/); break; case 0x71: case 0x72: case eIR_RFM: if (key_up) { /* JPF TODO ? */ } break; default: /* Test to see if we should enter the diagnostic state */ #ifdef OLD_INFO_KEY_PROCESSING if (key_up && (p_app->last_key_down == code) && !p_app->settings.av_mute) { GETTIMEOFDAY(&cur_tv); if ((p_app->last_key_tv.tv_sec + BAPP_DIAG_TIMEOUT) <= cur_tv.tv_sec) { if (bapp_enter_diag(p_app,code)) break; } } #endif if ((p_app->state != eAPP_STATE_NORMAL) && (p_app->screen_id == eSCREEN_BANNER)) break; screen_event.type = eS_EVENT_IR; screen_event.id = p_event->id; if (p_app->p_screens[p_app->screen_id].handle_event(p_app, (void*)&(p_app->p_screens[p_app->screen_id]),&screen_event)) { BDBG_MSG(("%s:%d screen_id %d\n",__FUNCTION__,__LINE__,p_app->screen_id)); /* Wait for vsync then do screen drawing */ bapp_new_screen(p_app); } else { bapp_handle_event(p_app,&screen_event); } } if (p_app->state != eAPP_STATE_FACTORY) { /* JPF TODO ? */ } if (!key_up) { BDBG_WRN(("%s: !key_up \n",__FUNCTION__)); p_app->last_key_down = code; GETTIMEOFDAY(&p_app->last_key_tv); } #ifndef OLD_INFO_KEY_PROCESSING else { BDBG_WRN(("%s: key_up \n",__FUNCTION__)); p_app->last_key_down = (bIR_codes_t)0xFFF; /* key up */ } #endif } /* Summary: Handle channel manager events. */ static bapp_state_t bapp_handle_chm_event(bapp_t *p_app) { bscreen_event_t screen_event; chm_event_t *p_event; struct timeval start_tv; unsigned short cur_ch; bapp_state_t new_state = p_app->state; /* Check for notification events */ if ((p_event = (chm_event_t*)bapp_task_pend_event(p_app->msg_queue,0)) != NULL) { screen_event.type = eS_EVENT_MAX; screen_event.id = p_event->id; /* if (p_event->type != eCHM_EVT_SIGNAL) */ { BDBG_WRN(("%s (%s)\n",__FUNCTION__,s_chm_evt_str[p_event->type])); } switch (p_event->type) { case eCHM_EVT_CANCEL: screen_event.type = eS_EVENT_CANCEL; break; case eCHM_EVT_DONE: screen_event.type = eS_EVENT_DONE; break; case eCHM_EVT_REDRAW: screen_event.type = eS_EVENT_REDRAW; break; case eCHM_EVT_PROGRESS: screen_event.type = eS_EVENT_PROGRESS; break; case eCHM_EVT_SIGNAL: { chm_signal_event_t *p_sig = (chm_signal_event_t*)p_event; p_app->lock = (p_sig->lock) ? true : false; p_app->signal_level = p_sig->power; p_app->tuned_freq = p_sig->freq_hz; p_app->snr = p_sig->SNR; p_app->mode = p_sig->mode; } break; case eCHM_EVT_DOWNLOAD: new_state = eAPP_STATE_DOWNLOAD; break; case eCHM_EVT_EAS: BDBG_MSG(("%s:%d EAS TIMER STARTED)\n",__FUNCTION__,__LINE__)); if (p_app->state == eAPP_STATE_NORMAL) { GETTIMEOFDAY(&start_tv); if (ch_map_set(bapp_cur_ch_map(p_app),p_event->id && 0xFFFF)) { static unsigned char *eas_buf = NULL; #ifndef EAS_BUF_LEN /* defined in chan_mgr_cable.c. Make sure they are match */ #define EAS_BUF_LEN (1024 + 188) #endif /* if 0, don't timeout */ if (p_event->id >> 16) p_app->eas_timeout = start_tv.tv_sec + (p_event->id >> 16); else p_app->eas_timeout = 0; bapp_set_current_screen(p_app, eSCREEN_BANNER, eSCREEN_MAX); bapp_tune(p_app); if (!eas_buf) { eas_buf = bapp_util_malloc(EAS_BUF_LEN); if (!eas_buf) { BDBG_ERR(("%s: out of memory)\n",__func__)); break; } } /* use a local buffer to avoid conflict when eas getting other data and while text scrolling in progress*/ memcpy(eas_buf, p_app->chm.eas_buf, EAS_BUF_LEN); if (BERR_SUCCESS == bscreen_eas_text_scrolling_info(p_app, eas_buf, EAS_BUF_LEN)) { p_app->eas_text = true; } } } break; case eCHM_EVT_CA: { chm_ca_event_t *p_ca_evt = (chm_ca_event_t*)p_event; if ((p_app->settings.CA_System_ID != p_ca_evt->CA_System_ID) || (p_app->settings.EMM_PID != p_ca_evt->EMM_PID)) { p_app->settings.CA_System_ID = p_ca_evt->CA_System_ID; p_app->settings.EMM_Provider_ID = p_ca_evt->EMM_Provider_ID; p_app->settings.SCP_operating_mode = p_ca_evt->SCP_operating_mode; p_app->settings.EMM_PID = p_ca_evt->EMM_PID; p_app->settings.has_CAT = true; p_app->settings.turn_on_freq_khz = p_ca_evt->freq_khz; p_app->settings_dirty = true; } } break; case eCHM_EVT_NETWORK: p_app->settings.network_PID = p_event->id; p_app->settings_dirty = true; break; case eCHM_EVT_STATUS: switch (p_event->id) { case eCHM_STATUS_HUNT: /* HUNT MODE */ BDBG_WRN(("eCHM_STATUS_HUNT\n")); break; case eCHM_STATUS_PENDING_INIT: /* Pending Init MODE */ BDBG_WRN(("eCHM_STATUS_PENDING_INIT\n")); new_state = eAPP_STATE_PENDING; break; case eCHM_STATUS_CHMAP: BDBG_WRN(("eCHM_STATUS_CHMAP flags = 0x%08x\n",p_event->ticks)); if (ch_map_cmp(&(p_app->settings.ch_map),&(p_app->chm.ch_map)) & p_event->ticks) { BDBG_WRN(("Channel map changed 0x%08x\n",p_event->ticks)); cur_ch = p_app->settings.ch_map.cur_ch; ch_map_copy(&(p_app->settings.ch_map),&(p_app->chm.ch_map),p_event->ticks); p_app->settings.ch_map.cur_ch = cur_ch; #ifdef BDBG_DEBUG_BUILD ch_output(bapp_cur_ch_map(p_app)); #endif p_app->settings_dirty = true; } else { BDBG_WRN(("Channel map NOT changed\n")); } if (p_app->settings.psi_scan) { p_app->settings.activated = 1; p_app->settings.timeout_cnt = 0xFFFF; } if (p_app->settings.activated) { if ((p_app->state == eAPP_STATE_NORMAL) && p_app->settings_dirty) { bapp_save_settings(p_app); /* if just scanned and have channnels, force to tune first channel */ if (p_app->settings.ch_map.vch_map.num_vch) { p_app->chm.force_tune = true; bapp_tune(p_app); } } else { new_state = eAPP_STATE_NORMAL; } } else { BDBG_WRN(("### ERROR recieve new channel map while still not activated - return to Hunt Mode\n")); new_state = eAPP_STATE_HUNT; } break; case eCHM_STATUS_PAT: p_app->has_PAT = true; #if 0 if (ch_map_cmp(&(p_app->settings.ch_map),&(p_app->chm.ch_map)) & p_event->ticks) { BDBG_WRN(("Channel map changed 0x%08x\n",p_event->ticks)); cur_ch = p_app->settings.ch_map.cur_ch; ch_map_copy(&(p_app->settings.ch_map),&(p_app->chm.ch_map),p_event->ticks); p_app->settings.ch_map.cur_ch = cur_ch; #ifdef BDBG_DEBUG_BUILD ch_output(bapp_cur_ch_map(p_app)); #endif p_app->settings_dirty = true; } else { BDBG_WRN(("Channel map NOT changed\n")); } #endif break; case eCHM_STATUS_NOPAT: p_app->has_PAT = false; break; case eCHM_STATUS_TUNE_MS: p_app->tune_ms = p_event->ticks; break; case eCHM_STATUS_SI_MS: p_app->si_ms = p_event->ticks; break; } break; case eCHM_EVT_AV_MUTE: bapp_standby(p_app,!p_event->id); break; case eCHM_EVT_ACTIVATION: { bool current_activation = p_app->settings.activated; p_app->settings.activated = p_event->id; if (p_app->settings.activated != current_activation) { p_app->settings_dirty = true; if (!p_app->settings.activated) { p_app->settings.deactivated = 1; new_state = eAPP_STATE_HUNT; } else { p_app->settings.deactivated = 0; if (p_app->settings.timeout_policy == 1) { p_app->settings.timeout_cnt = p_app->settings.timeout; } #ifdef BDBG_DEBUG_BUILD /* Some default value to prevent immediate de-activation in lab environments */ else if (p_app->settings.timeout == 0) { p_app->settings.timeout_cnt += 4; } #endif } } } break; case eCHM_EVT_VCT_ID: { unsigned short current_vct_id = p_app->settings.VCT_ID; p_app->settings.VCT_ID = p_event->id; if ((p_app->settings.VCT_ID != current_vct_id) && (CONFIG_DEF_VCT_ID != 0xFFFF)) { BDBG_WRN(("%s:%d Configuration VCT_ID %d does not match current value %d\n", __FUNCTION__,__LINE__,p_app->settings.VCT_ID,current_vct_id)); p_app->settings.activated = false; new_state = eAPP_STATE_PENDING; } p_app->settings_dirty = true; } break; case eCHM_EVT_CONFIGURATION: { chm_config_event_t *p_cfg_evt = (chm_config_event_t*)p_event; unsigned short current_vct_id = p_app->settings.VCT_ID; p_app->settings.VCT_ID = p_cfg_evt->VCT_ID; p_app->settings.timeout = p_cfg_evt->timeout; p_app->settings.timeout_policy = p_cfg_evt->timeout_policy; p_app->settings.max_location_errors = p_cfg_evt->max_location_errors; if (p_app->settings.timeout_policy == 0) { p_app->settings.timeout_cnt = p_cfg_evt->timeout; } p_app->settings.location = p_cfg_evt->location; if (p_app->have_time) { p_app->settings.message_time = p_app->system_time; } else { BDBG_WRN(("%s:%d Need system time before configuration can be processed\n",__FUNCTION__,__LINE__)); break; } if ((p_app->settings.VCT_ID != current_vct_id) && (CONFIG_DEF_VCT_ID != 0xFFFF)) { BDBG_WRN(("%s:%d Configuration VCT_ID %d does not match current value %d\n", __FUNCTION__,__LINE__,p_app->settings.VCT_ID,current_vct_id)); p_app->settings.activated = false; new_state = eAPP_STATE_PENDING; } p_app->settings_dirty = true; } break; case eCHM_EVT_LOCATION: if(p_app->settings.psi_scan) break; p_app->location = p_event->id; if (p_app->settings.location != p_event->id) { if (p_app->settings.location_errors > p_app->settings.max_location_errors) { p_app->settings.activated = false; new_state = eAPP_STATE_HUNT; } else { p_app->settings.location_errors++; p_app->settings_dirty = true; } } else { if (p_app->settings.timeout_policy == 0) { p_app->settings.timeout_cnt = p_app->settings.timeout; } } break; case eCHM_EVT_TIME: { chm_time_event_t *p_time_evt = (chm_time_event_t*)p_event; p_app->system_time = p_time_evt->system_time; p_app->system_offset = p_time_evt->system_offset; p_app->settings.utc_offset = (int)p_time_evt->utc_offset; p_app->have_time = true; } break; case eCHM_EVT_TIMEZONE: { chm_tz_event_t *p_tz_evt = (chm_tz_event_t*)p_event; if (p_tz_evt->utc_offset & 0x10000) p_app->settings.utc_offset = (int)((0xFFFF & p_tz_evt->utc_offset) | 0xFFFF0000); else p_app->settings.utc_offset = (int)(0xFFFF & p_tz_evt->utc_offset); p_app->settings.dst_delta = p_tz_evt->dst_delta; p_app->settings.dst_entry = p_tz_evt->dst_entry; p_app->settings.dst_exit = p_tz_evt->dst_exit; p_app->settings_dirty = true; } break; case eCHM_EVT_PHONENUMBER: if (bapp_util_strcmp((char*)&(p_app->settings.phonenumber),p_event->data) != 0) { if (bapp_util_strlen(p_event->data) > MAX_PHONE_LEN) p_event->data[MAX_PHONE_LEN-1] = 0; bapp_util_strcpy((char*)&(p_app->settings.phonenumber),p_event->data); p_app->settings_dirty = true; } break; case eCHM_EVT_RESET: switch (p_event->id) { default: case 0: /* nop */ break; case 1: /* reset */ bapp_do_poweroff(p_app); break; case 2: /* reset some */ bapp_reset_settings(p_app); bapp_save_settings(p_app); bapp_do_poweroff(p_app); break; case 3: /* reset full */ bapp_reset_settings(p_app); bapp_save_settings(p_app); /* Erase downloaded firmware here */ bapp_do_poweroff(p_app); break; } break; case eCHM_EVT_SAVE: /* Must be careful of when this is done if running from flash */ if ((p_app->settings.audio_vol != p_app->audio_vol) || p_app->settings_dirty) { p_app->settings.audio_vol = p_app->audio_vol; bapp_save_settings(p_app); } break; } BDBG_MSG(("%s:%d CHM EVENT = %d)\n",__FUNCTION__,__LINE__,p_event->type)); if (p_app->p_screens[p_app->screen_id].handle_event(p_app, (void*)&(p_app->p_screens[p_app->screen_id]),&screen_event)) { BDBG_WRN(("%s:%d screen_id %d\n",__FUNCTION__,__LINE__,p_app->screen_id)); /* Wait for vsync then do screen drawing */ bapp_new_screen(p_app); } else { bapp_handle_event(p_app,&screen_event); } } return new_state; } /* Summary: Main event loop when in normal operation mode. */ static bapp_state_t bapp_run_main(bapp_t *p_app) { bapp_event_t event; bscreen_event_t screen_event; /* Check for UI event */ if (bapp_remote_get_event(p_app->remote,&event) == eBAPP_RESULT_OK) { p_app->last_keypress_tick = bapp_task_getticks(); bapp_handle_user_event(p_app,&event); } /* Yield control */ bapp_util_sleep(p_app->yield_ms); screen_event.type = eS_EVENT_IDLE; screen_event.id = 0; /* Give screen idle time to handle menu timeouts, etc */ if (p_app->p_screens[p_app->screen_id].handle_event(p_app, (void*)&(p_app->p_screens[p_app->screen_id]),&screen_event)) { /* Wait for vsync then do screen drawing */ bapp_new_screen(p_app); } /* Do application idle time processing */ bapp_idle(p_app); return p_app->state; } #if 0 /* Summary: Main event loop when in normal operation mode. */ static bapp_state_t bapp_pending(bapp_t *p_app) { bscreen_event_t screen_event; unsigned short num_vch, num_st; unsigned char num_freq ; bapp_event_t event; unsigned event_cnt; /* Check for UI event */ if (buser_input_get_event(p_app->user_io,&event,1,&event_cnt) == b_ok) { if (event_cnt > 0) { p_app->last_keypress_tick = bapp_task_getticks(); bapp_handle_user_event(p_app,&event); } } screen_event.type = eS_EVENT_IDLE; screen_event.id = 0; /* Give screen idle time to handle menu timeouts, etc */ if (p_app->p_screens[p_app->screen_id].handle_event(p_app, (void*)&(p_app->p_screens[p_app->screen_id]),&screen_event)) { /* do screen drawing */ bapp_new_screen(p_app); } /* Yield control */ bapp_util_sleep(p_app->yield_ms); /* Check to see if there is a channel map */ ch_map_get_counts(bapp_cur_ch_map(p_app),&num_vch,&num_st,&num_freq); #if 0 if ((num_vch > 0) && (p_app->settings.activated || (p_app->settings.VCT_ID == 0))) { BDBG_WRN(("%s:%d Switch to NORMAL State(%d,%d,%d)\n",__FUNCTION__,__LINE__, num_vch,p_app->settings.activated,p_app->settings.VCT_ID)); return eAPP_STATE_NORMAL; } #endif return p_app->state; } #endif /* Summary: Main event loop when in normal operation mode. */ static bapp_state_t bapp_downloading(bapp_t *p_app) { /* Yield control */ bapp_util_sleep(p_app->yield_ms); return(p_app->chm.cmd == eCHM_DOWNLOAD) ? eAPP_STATE_DOWNLOAD : eAPP_STATE_NORMAL; } /* Summary: Perform a channel hunt (for cable ). */ static int bapp_channel_hunt(bapp_t *p_app) { p_app->chm_cmd.cmd_id = eCHM_HUNT; chm_cmd(&p_app->chm,&p_app->chm_cmd); return 0; } /* Summary: Main app loop. Description: Main application event loop. */ #ifdef BDBG_DEBUG_BUILD static const char *s_app_state_name[] = { "DEFAULT","HUNTING","PENDING","NORMAL","DOWNLOAD","FACTORY","DONE"}; #endif static void bapp_eval_state(bapp_t *p_app,bapp_state_t new_state) { bapp_led_set_mode(p_app); led_update(APP_DEFAULT_YIELD); if (p_app->state == new_state) return; BDBG_WRN(("from %s to %s\n",s_app_state_name[p_app->state], s_app_state_name[new_state] )); /* Handle exiting current state */ switch (p_app->state) { case eAPP_STATE_PENDING: if ((new_state == eAPP_STATE_NORMAL) && p_app->settings_dirty) { bapp_save_settings(p_app); } p_app->last_screen_id = eSCREEN_MAX; memset(&p_app->cur_vch,0,sizeof(p_app->cur_vch)); break; case eAPP_STATE_HUNT: bapp_screen_redraw(p_app); bapp_task_sleep(250); /* must display full progress bar for at least 250ms before proceding */ case eAPP_STATE_DOWNLOAD: case eAPP_STATE_NORMAL: case eAPP_STATE_FACTORY: case eAPP_STATE_DEFAULT: case eAPP_STATE_DONE: break; } /* Handle entering new state */ switch (new_state) { case eAPP_STATE_HUNT: if (p_app->screen_id < eSCREEN_DIAG_MENU) { if (p_app->power) bapp_set_current_screen(p_app, eSCREEN_BANNER, eSCREEN_MAX); else bapp_set_current_screen(p_app, eSCREEN_POWER_OFF, eSCREEN_MAX); bapp_new_screen(p_app); } bapp_channel_hunt(p_app); break; case eAPP_STATE_NORMAL: if (p_app->screen_id < eSCREEN_DIAG_MENU) { if (p_app->power) bapp_set_current_screen(p_app, eSCREEN_BANNER, eSCREEN_MAX); else bapp_set_current_screen(p_app, eSCREEN_POWER_OFF, eSCREEN_MAX); } bapp_new_screen(p_app); bapp_tune(p_app); break; case eAPP_STATE_DOWNLOAD: if (p_app->screen_id < eSCREEN_DIAG_MENU) { if (p_app->power) bapp_set_current_screen(p_app, eSCREEN_BANNER, eSCREEN_MAX); else bapp_set_current_screen(p_app, eSCREEN_POWER_OFF, eSCREEN_MAX); bapp_new_screen(p_app); } p_app->chm_cmd.cmd_id = eCHM_DOWNLOAD; chm_cmd(&p_app->chm,&p_app->chm_cmd); break; case eAPP_STATE_FACTORY: bapp_set_current_screen(p_app, eSCREEN_STATUS, eSCREEN_MAX); bapp_new_screen(p_app); break; case eAPP_STATE_PENDING: case eAPP_STATE_DEFAULT: case eAPP_STATE_DONE: break; } /* Make new state the current state */ p_app->state = new_state; switch (new_state) { case eAPP_STATE_PENDING: case eAPP_STATE_NORMAL: bapp_screen_redraw(p_app); break; default: break; } } /* Summary: Main app loop. Description: Main application event loop. */ void bapp_run(bapp_t *p_app) { bapp_state_t new_state; BDBG_WRN(("%s:%d - Start running.\n",__FUNCTION__,__LINE__)); bapp_screen_redraw(p_app); while (p_app->state != eAPP_STATE_DONE) { new_state = p_app->state; switch (p_app->state) { case eAPP_STATE_HUNT: case eAPP_STATE_PENDING: case eAPP_STATE_NORMAL: case eAPP_STATE_FACTORY: new_state = bapp_run_main(p_app); break; case eAPP_STATE_DOWNLOAD: new_state = bapp_downloading(p_app); break; case eAPP_STATE_DEFAULT: case eAPP_STATE_DONE: /* Yield control */ bapp_util_sleep(p_app->yield_ms); break; } /* Check for chm event */ if (new_state == p_app->state) new_state = bapp_handle_chm_event(p_app); bapp_eval_state(p_app,new_state); } /* finish */ bapp_nexus_close(&p_app->nexus); } /* Summary: Return current application channel map. */ ch_map_t *bapp_cur_ch_map(bapp_t *p_app) { return &(p_app->settings.ch_map); } /* Summary: Tune to the current frequency and get the VCT. Description: Tune to the current frequency and get the VCT. Returns non-zero on failure. */ int bapp_change_channel(bapp_t *p_app, int ch_up, int tune) { int retval = -1; bool result; BDBG_MSG(("%s (%d,%d)\n",__FUNCTION__,ch_up,tune)); if (ch_up) { result = ch_map_set_next(bapp_cur_ch_map(p_app)); } else { result = ch_map_set_prev(bapp_cur_ch_map(p_app)); } if (!result) { BDBG_ERR(("%s (%d,%d) failed \n",__FUNCTION__,ch_up,tune)); return retval; } if (tune) { retval = bapp_tune(p_app); p_app->settings_dirty = true; } else { retval = 0; } return retval; } /* Summary: Tune to the specified channel. Description: Tune to the specified channel or nearest to the channel. Returns non-zero on failure. */ int bapp_set_channel(bapp_t *p_app, int channel) { int retval = -1; vch_t cur_vch; st_t cur_st; freq_t cur_freq; BDBG_WRN(("%s (%d)\n",__FUNCTION__,channel)); if (!ch_map_get_current(bapp_cur_ch_map(p_app),&cur_vch,&cur_st,&cur_freq)) { BDBG_WRN(("%s Invalid current channel or map.\n",__FUNCTION__)); return 0; } #if 1 if (!ch_map_is_channel_visible(bapp_cur_ch_map(p_app),(unsigned short)channel)) { BDBG_WRN(("%s Hidden channel %d\n",__FUNCTION__,channel)); return 0; } #endif if (ch_map_set_ch(bapp_cur_ch_map(p_app),(unsigned short)channel)) { p_app->settings_dirty = true; retval = bapp_tune(p_app); } return retval; } /* Summary: Tune to the current channel. */ int bapp_tune(bapp_t *p_app) { vch_t new_vch; st_t new_st; freq_t new_freq; if (p_app->settings.av_mute) return 0; p_app->prev_vch = p_app->cur_vch; /* check to see if the current channel info has changed */ if (p_app->decoding) { if (!ch_map_get_current(bapp_cur_ch_map(p_app),&new_vch,&new_st,&new_freq)) { BDBG_WRN(("%s Invalid current channel or map.\n",__FUNCTION__)); return 0; } if ((new_freq.freq_khz == p_app->cur_freq.freq_khz) && (new_vch.program_num == p_app->cur_vch.program_num)) { BDBG_WRN(("%s Channel info did not change skip re tune.\n",__FUNCTION__)); return 0; } BDBG_WRN(("%s Channel Navigation Changed - Retune\n",__FUNCTION__)); } p_app->lock = false; p_app->signal_level = 0; p_app->tune_cmd.cmd_id = eCHM_TUNE; p_app->tune_cmd.type = eCHM_CURRENT; chm_cmd(&p_app->chm,(chm_cmd_event_t*)&p_app->tune_cmd); p_app->decoding = true; p_app->last_tune_tick = bapp_task_getticks(); if (!ch_map_get_current(bapp_cur_ch_map(p_app),&p_app->cur_vch,&p_app->cur_st,&p_app->cur_freq)) { BDBG_WRN(("%s Invalid current channel or map.\n",__FUNCTION__)); return 0; } return 0; } /* Summary: Tune to the current channel. */ int bapp_tune_prev(bapp_t *p_app) { return bapp_set_channel(p_app,p_app->prev_vch.ch_num); } #if !defined(RAM_BUILD) /* Summary: Stop decoding audio and video */ static int bapp_stop_decode(bapp_t *p_app) { p_app->chm_cmd.cmd_id = eCHM_STOP; chm_cmd(&p_app->chm,&p_app->chm_cmd); p_app->decoding = false; return 0; } #endif /* Summary: Restart audio decode (after SAP swap audio PID) */ int bapp_rotate_audio_sap(bapp_t *p_app) { vch_t vch; st_t st; freq_t freq; if (p_app->settings.av_mute || !p_app->decoding) return 0; if (!ch_map_get_current(bapp_cur_ch_map(p_app),&vch,&st,&freq)) { BDBG_WRN(("%s Invalid current channel or map.\n",__FUNCTION__)); return 0; } if (vch.num_audio <= 1) return 0; vch.cur_audio++; if ( vch.cur_audio >= vch.num_audio) { vch.cur_audio = 0; } if (!ch_map_vch_update(bapp_cur_ch_map(p_app),&vch)) { BDBG_WRN(("%s Error updating channel map.\n",__FUNCTION__)); return 0; } p_app->chm_cmd.cmd_id = eCHM_SAP; chm_cmd(&p_app->chm,&p_app->chm_cmd); return 0; } /* Summary: set CLUT */ void bapp_set_palette(bapp_t *p_app, bapp_palette_t palette) { unsigned int *clut; /* [out] address of palette */ unsigned int size; switch (palette) { case ePALETTE_SCREENSAVER: bapp_palette_get(ePALETTE_LOGO,&clut,&size); break; case ePALETTE_DEFAULT: default: bapp_palette_get(ePALETTE_APP,&clut,&size); break; } bapp_util_memcpy(p_app->nexus.mem[p_app->nexus.surface_idx].palette,clut, size); NEXUS_Surface_Flush(p_app->nexus.surface[p_app->nexus.surface_idx]); } /* Summary: returns true is sufficient signal strength, false otherwise always returns true for TUNE_DURATION msecs after tuning begins. */ bool bapp_get_signal_status(bapp_t *p_app) { #if 1 /* JPF TODO */ return true; #else static bool first_call = true; static bool good_signal = true; static uint32_t last_video_pts = 0; static uint32_t last_video_pts_ticks = 0; static uint32_t last_audio_pts = 0; vch_t vch; st_t st; freq_t freq; bdecode_config cfg; bdecode_status status; uint32_t current_ticks = bapp_task_getticks(); /* If the decode is stopped dont report bad signal */ if (p_app->decoding == false) return true; /* check for tune duration timeout - this is used to restrict display of the 'no signal' popup for a period of time (TUNE_DURATION) after tuning */ if (current_ticks < (p_app->last_tune_tick + MS_TO_TICKS(TUNE_DURATION))) return true; if (!ch_map_get_current(bapp_cur_ch_map(p_app),&vch,&st,&freq)) return false; /* if first time calling function get baseline video pts * then wait 500msecs and continue. since we require 2 samples * of decoder status at least 500msecs apart to determine signal * status, we need to "prime the pump" on the very first call only */ if (first_call) { first_call = false; status.audio_decode = p_app->audio; bdecode_get_status(p_app->decode,&status); last_video_pts = status.video_pts; last_video_pts_ticks = current_ticks; bapp_task_sleep(500); current_ticks = bapp_task_getticks(); last_audio_pts = status.audio_pts; } /* make sure video pts is changing - indicates successful decoding. * only check every 500msecs or so */ if ((current_ticks - last_video_pts_ticks) > MS_TO_TICKS(500)) { status.audio_decode = p_app->audio; bdecode_get_status(p_app->decode,&status); /* if pts is changing, we are decoding properly */ good_signal = (last_video_pts != status.video_pts); if (good_signal == false) { good_signal = (last_audio_pts != status.audio_pts); } last_audio_pts = status.audio_pts; BDBG_MSG(("good_signal:%d vpts:0x%08x last_vpts:0x%08x ticks:%d", good_signal, status.video_pts, last_video_pts, current_ticks)); last_video_pts = status.video_pts; last_video_pts_ticks = current_ticks; } bdecode_get_config(p_app->decode,&cfg); if (!good_signal) { if (cfg.channel_change != 0) { cfg.mute = 1; cfg.channel_change = 0; bdecode_set_config(p_app->decode,&cfg); } } else { if (cfg.channel_change != 2) { cfg.mute = 0; cfg.channel_change = 2; bdecode_set_config(p_app->decode,&cfg); } } return good_signal; #endif } /* Summary: Return the current screen. */ bscreen_t * bapp_get_current_screen(bapp_t *p_app) { return &p_app->p_screens[p_app->screen_id]; } /* Summary: Return the application reference. */ bapp_t *bapp_get_app(void) { return s_p_app; } bapp_result_t bapp_post_event(bapp_task_event_t event) { BDBG_WRN(("%s 0x%08x\n",__FUNCTION__,event)); return bapp_task_post_event(s_p_app->msg_queue,event); }