/*************************************************************************** * 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 "chan_mgr.h" #include "bstd.h" #include "ts_psi.h" #include "ts_psi.h" #include "ts_pat.h" #include "ts_pmt.h" #include "bsettop_message.h" #include "bapp.h" #include "psip_ett.h" #include "bsmart.h" #include "bcrc32.h" #include "bsettop_smessage.h" BDBG_MODULE(chan_mgr); /* Register software module with debug interface */ /* PR32033 fix, for ETT 10.5- 10.10, need more buffer and timeout */ #define CHM_MIN(x,y) ((x < y) ? x : y) //#define DEBUG_RRT #ifdef BCM_DEBUG static const char *s_chm_cmd_name[] = { "eCHM_CANCEL","eCHM_SCAN","eCHM_TUNE","eCHM_INFO","eCHM_GUIDE","eCHM_STOP","eCHM_CHECK_SIGNAL"}; static const char *s_chm_state[] = { "eCHM_GET_STT","eCHM_GET_VCT","eCHM_GET_MGT","eCHM_PROCESS_MGT","eCHM_PROG_IDLE","eCHM_GET_RRT"}; #endif const char g_tz_map[] = { 0, 5, 6, 7, 8, 0}; #define CHM_CHECK_SIGNAL_FREQ 1000 #define PROG_INFO_TIMEOUT (5 * g_ticks_per_second) #define CHM_INT_STK_SIZE 1024 /* in words */ #define VCT_BUF_LEN (1024 + 188) #define PAT_BUF_LEN (1024 + 188) #define PMT_BUF_LEN (1024 + 188) #define STT_BUF_LEN (256 + 188) #define EIT_BUF_LEN (4400 + 188) #define MGT_BUF_LEN (4400 + 188) #define ETT_BUF_LEN (4400 + 188) #define CHM_IDLE_TIMEOUT 10 #define VCT_TIMEOUT 1500 #define PAT_TIMEOUT 800 #define PMT_TIMEOUT 800 #define MSC_TIMEOUT 800 #define MGT_TIMEOUT 500 #define STT_TIMEOUT 1200 #define VCT_ETT_TIMEOUT 1200 #define ETT_TIMEOUT 2000 #define EIT_TIMEOUT 2000 #define RRT_TIMEOUT 5000 /* in milliseconds */ #define CHM_MUTEX_TIMEOUT 10 #define MAX_TIME_SLOTS 4 #define MGT_CYCLE_TIMEOUT 1 /* in seconds */ #define RRT_CYCLE_TIME (15) /* in seconds */ #define CHM_MIN(x,y) ((x < y) ? x : y) typedef struct iso_lang_map_t { char iso_lang_code[3]; bapp_lang_t lang; }iso_lang_map_t; typedef enum chm_err_t { eCHM_ERR_OK = 0, eCHM_ERR_FAILED = -1, eCHM_ERR_TIMEOUT = -2, eCHM_ERR_CANCELED = -3 }chm_err_t; const static iso_lang_map_t s_iso_lang_map[] = { { { 'e','n','g'},eLANG_ENGLISH}, { { 'f','r','a'},eLANG_FRENCH}, { { 'f','r','e'},eLANG_FRENCH}, { { 'e','s','l'},eLANG_SPANISH}, { { 's','p','a'},eLANG_SPANISH}, }; const static int s_iso_lang_map_num = sizeof(s_iso_lang_map)/sizeof(iso_lang_map_t); #ifdef BQAM_SCRIPT /* frequencies from the channel number i.e. 2 - 69 in MHz*/ const unsigned short s_ch_to_freq[133] = { 57, 63, 69, 79, /* 2 - 5 VHF-L 2 - 6 */ 85, 177, 183, 189, /* 6 - 9 VHF-H 7 - 13*/ 195, 201, 207, 213, /* 10 - 13 */ 123, 129, 135, 141, 147, 153, 159, 165, 171, 219, 225, 231, 237, 243, 249, 255, 261, 267, 273, 279, 285, 291, 297, 303, 309, 315, 321, 327, 333, 339, 345, 351, 357, 363, 369, 375, 381, 387, 393, 399, 405, 411, 417, 423, 429, 435, 441, 447, 453, 459, 465, 471, 477, 483, 489, 495, 501, 507, 513, 519, 525, 531, 537, 543, 547, 555, 561, 567, 573, 579, 585, 591, 597, 603, 609, 615, 621, 627, 633, 639, 645, 93, 99, 105, 111, 117, 651, 657, 663, 669, 675, 681, 687, 693, 699, 705, 711, 717, 723, 729, 735, 741, 747, 753, 759, 765, 771, 777, 783, 789, 795, 801, 813, 819, 825, 831, 837, 843, 849, 855, 861 }; #else /* frequencies from the channel number i.e. 2 - 69 in MHz*/ const unsigned short s_ch_to_freq[68] = { 57, 63, 69, 79, /* 2 - 5 VHF-L 2 - 6 */ 85, 177, 183, 189, /* 6 - 9 VHF-H 7 - 13*/ 195, 201, 207, 213, /* 10 - 13 */ 473, 479, 485, 491, /* 14 - 17, 14 - Begin UHF */ 497, 503, 509, 515, /* 18 - 21 */ 521, 527, 533, 539, /* 22 - 25 */ 545, 551, 557, 563, /* 26 - 29 */ 569, 575, 581, 587, /* 30 to 33 */ 593, 599, 605, 611, /* 34 to 37 */ 617, 623, 629, 635, /* 38 to 41 */ 641, 647, 653, 659, /* 42 to 45 */ 665, 671, 677, 683, /* 46 to 49 */ 689, 695, 701, 707, /* 50 to 53 */ 713, 719, 725, 731, /* 54 to 57 */ 737, 743, 749, 755, /* 58 to 61 */ 761, 767, 773, 779, /* 62 to 65 */ 785, 791, 797, 803, /* 66 to 69 */ }; #endif const unsigned int s_ch_num = (sizeof(s_ch_to_freq)/sizeof(s_ch_to_freq[0])); #define MAX_MAJOR_CH s_ch_num static unsigned char s_freq_list[256]; static int s_freq_cnt = 0; /* useful for byte swapping PID */ typedef struct tsp_tmp_t { unsigned int sync_byte : 8; unsigned int PID : 16; unsigned int other : 8; }tsp_tmp_t; #define SWAP_SHORT(x) ((((unsigned short)x & 0xFF) << 8) | (((unsigned short)x & 0xFF00) >> 8)) /* This if or Little Endian only and bitfield ordering can change in some compilers*/ typedef struct tsp_t { unsigned int sync_byte : 8; unsigned int PID : 13; unsigned int transport_priority : 1; unsigned int payload_unit_start_indicator : 1; unsigned int transport_error_indicator : 1; unsigned int continuity_counter : 4; unsigned int adaptation_field_control : 2; unsigned int transport_scrambling_control : 2; }tsp_t; /* Local function prototypes */ static void chm_scan(chm_mgr_t *p_chm); static int chm_get_prog(chm_mgr_t *p_chm, bapp_ch_t *p_ch, chm_info_t *p_info /* info structure to populate */ ); static int chm_get_prog_details(chm_mgr_t *p_chm); static int chm_post_app_event(chm_mgr_t *p_chm, /* Channel manager reference */ chm_event_t *p_event /* Event to post to app queue */ ); static int chm_stop_decode(chm_mgr_t *p_chm); static int chm_tune_current_freq(chm_mgr_t *p_chm); #define DBG_DUMP #ifdef DBG_DUMP static void DBG_HEXDUMP(unsigned char* ptr, unsigned long len) { unsigned long i; for (i=0; igot_stt) return eCHM_ERR_FAILED; gettimeofday(&tv); *time = p_chm->sys_time + (tv.tv_sec - p_chm->sys_offset); return 0; } /* Summary: Check for cancel, returns true if a cancel occured. */ #ifdef BCM_DEBUG static int chm_check_cancel_ex(chm_mgr_t *p_chm ,const char* func, int line) #define chm_check_cancel(chm) chm_check_cancel_ex(chm,__FUNCTION__,__LINE__) #else static int chm_check_cancel(chm_mgr_t *p_chm ) #endif { unsigned int cmd; if (p_chm->cmd == eCHM_CANCEL) return 1; cmd = (unsigned int)bos_pend_event(p_chm->queue,0); if (cmd == (eCHM_CANCEL + 1)) { p_chm->cmd = (chm_cmd_t)(cmd - 1); BDBG_WRN(("Command canceled(%s:%d)...\n",func,line)); /* Send message to app */ p_chm->chm_evt.type = eCHM_EVT_CANCEL; p_chm->chm_evt.id = 0; chm_post_app_event(p_chm,&p_chm->chm_evt); } else if (cmd > 0) { BDBG_WRN(("Throwing away command %d(%s:%d)...\n", (cmd - 1),func,line)); } return(p_chm->cmd == eCHM_CANCEL) ? 1 : 0; } /* Summary: Send current signal status. Return non-zero on failure. */ static void chm_send_status(chm_mgr_t *p_chm,unsigned int freq, bool locked) { btuner_ds_status ds_status; chm_signal_event_t *p_sig = &(p_chm->signal_evt); bapp_t *p_app = (bapp_t*)p_chm->p_app; memset(&(p_chm->signal_evt),0,sizeof(chm_signal_event_t)); p_sig->type = eCHM_EVT_SIGNAL; if (!locked) { p_sig->freq_hz = freq; } else if (btuner_get_ds_status(p_app->tuner,&ds_status) == b_ok) { /* Send message to app */ p_sig->freq_hz = ds_status.freq; p_sig->SNR = ds_status.snr; p_sig->power = ds_status.power; p_sig->qam_b_mode = ds_status.mode; p_sig->lock = ds_status.lock; } chm_post_app_event(p_chm,(chm_event_t*)&(p_chm->signal_evt)); } static void * chm_smessage_callback(void *context, size_t data_size) { chm_mgr_t *p_chm = (chm_mgr_t *)context; /* ignore return value */ bos_post_event(p_chm->gen_queue, (bapp_task_event_t*)data_size); return NULL; } static void* chm_smessage_overflow(void * context, size_t data_size) { return NULL; } static int chm_getmessage(chm_mgr_t *p_chm, /* Channel manager reference */ void *buf, /* buffer to copy section into */ unsigned int *len, /* [in] max size of buffer, [out] section size copied into buffer */ unsigned int timeout /* timeout in milliseconds */ ) { bresult bres; size_t msg_size; int result = eCHM_ERR_FAILED; bapp_t *p_app = (bapp_t*)p_chm->p_app; smessage_stream_params_t params; smessage_stream_params_init(¶ms, p_app->smsg); params.band = p_app->msg_params.band; params.pid = p_app->msg_params.pid; params.filter = p_app->msg_params.filter; params.buffer = buf; params.buffer_size = *len; params.crc_disabled = p_app->msg_params.psi_crc_disabled; params.data_ready_callback = chm_smessage_callback; params.overflow = chm_smessage_overflow; params.callback_context = (void *)p_chm; params.priv = (void *)4; BDBG_MSG(("pid %x fil %x tim %u len %u", params.pid, params.filter.coef[0], timeout, *len)); bres = smessage_start(¶ms, p_app->smsg); if(b_ok != bres){ BDBG_ERR(("%s:%d",__FILE__, __LINE__)); goto ExitFunc; } msg_size = (size_t) bos_pend_event(p_chm->gen_queue, timeout); if (0 == msg_size){ result = eCHM_ERR_TIMEOUT; }else{ *len = msg_size; result = eCHM_ERR_OK; } bres = smessage_stop(p_app->smsg); if(b_ok != bres){ BDBG_ERR(("%s:%d",__FILE__, __LINE__)); result = eCHM_ERR_FAILED; goto ExitFunc; } ExitFunc: return result; } #define CHM_SC_MASK 0xc0 #define CHM_SC_BUFFER_SIZE (188*10) static unsigned char chm_sc_buffer[CHM_SC_BUFFER_SIZE]; static void * chm_sc_callback(void * c, size_t msg_size) { unsigned int * pdata_ready = (unsigned int *)c; *pdata_ready = msg_size; return NULL; } bool chm_check_sc(chm_mgr_t *p_chm, bband_t band, unsigned short pid) { smessage_stream_t ms; smessage_stream_params_t params; bresult bres; bool result; unsigned int count; volatile unsigned int data_ready; unsigned char * walker; result = false; ms = smessage_open(smessage_format_tsc); if (NULL == ms) { BDBG_ERR(("%s : smessage_open", __PRETTY_FUNCTION__)); goto ExitFunc; } smessage_stream_params_init(¶ms, ms); params.band = band; params.pid = pid; params.buffer_size = CHM_SC_BUFFER_SIZE; params.buffer = chm_sc_buffer; params.data_ready_callback = chm_sc_callback; params.overflow = chm_sc_callback; params.callback_context = (void *)&data_ready; data_ready = 0; bres = smessage_start(¶ms, ms); if (b_ok != bres) { smessage_close(ms); BDBG_ERR(("%s : smessage_start", __PRETTY_FUNCTION__)); goto ExitFunc; } /* Updating unsigned int is atomic operation on mips. We can just wait for callback to update our flag when data is ready */ count = 3; do { bos_sleep(50); count--; }while ((0 == data_ready)&&(0 != count)); bres = smessage_stop(ms); if (b_ok != bres) { smessage_close(ms); BDBG_ERR(("%s : smessage_stop", __PRETTY_FUNCTION__)); goto ExitFunc; } smessage_close(ms); /* Figure out if we got data and if sc flags are set */ if (0 != data_ready) { /* We have data. Check few packets for sc bits */ walker = chm_sc_buffer; for (count = 0; count < 5; count++) { if (0 != (walker[3] & CHM_SC_MASK)) { goto ExitFunc; } walker += 188; } result = true; } ExitFunc: BDBG_ERR(("pid %d %s", pid, (result == true ? "clear" : "encr"))); return result; } /* Summary: Add a chanell to the channel map. Description: Add the channel to the channel map in sorted order. */ static void chm_add_channel(chm_mgr_t *p_chm, /* Channel manager reference */ bapp_ch_t *p_ch ) { int idx,idx2, found_slot; bapp_t *p_app = (bapp_t*)p_chm->p_app; found_slot = 0; for (idx = 0; idx < p_app->settings.num_channels; idx++) { if ((p_ch->major < p_app->settings.ch[idx].major) || ((p_ch->major == p_app->settings.ch[idx].major) && (p_ch->minor < p_app->settings.ch[idx].minor))) { for (idx2 = p_app->settings.num_channels - 1; idx2 >= idx; idx2--) { BDBG_MSG(("move %d.%d from %d to %d\n",p_app->settings.ch[idx2].major,p_app->settings.ch[idx2].minor, idx2, idx2 + 1)); memcpy(&p_app->settings.ch[idx2 + 1],&p_app->settings.ch[idx2], sizeof(bapp_ch_t)); } memcpy(&p_app->settings.ch[idx ],p_ch, sizeof(bapp_ch_t)); BDBG_MSG(("Add %d.%d at %d\n",p_ch->major,p_ch->minor, idx)); p_app->settings.ch[idx].cmd = p_chm->ant_cmd; found_slot = 1; break; } } if (!found_slot) { memcpy(&p_app->settings.ch[p_app->settings.num_channels],p_ch, sizeof(bapp_ch_t)); p_app->settings.ch[p_app->settings.num_channels].cmd = p_chm->ant_cmd; BDBG_MSG(("Append %d.%d at %d\n",p_ch->major,p_ch->minor, p_app->settings.num_channels)); } p_app->settings.num_channels++; } /* Summary: Check for a valid language code for the current language settings. Returns the bapp_lang_t number or < 0 if not a valid language. */ static int chm_valid_lang(chm_mgr_t *p_chm, /* Channel manager reference */ char *p_code /* Three byte iso 639-2 language code */ ) { int idx; for (idx = 0; idx < s_iso_lang_map_num; ++idx) { char *code = s_iso_lang_map[idx].iso_lang_code; if ((p_code[0] == code[0]) && (p_code[1] == code[1]) && (p_code[2] ==code[2])) { return s_iso_lang_map[idx].lang; } } BDBG_MSG(("ISO 639-2 language not found %c%c%c\n",p_code[0],p_code[1],p_code[2])); return eCHM_ERR_FAILED; } /* Summary: Get the program info for the program. Description: Get the PMT for the program. Returns non-zero on failure. */ static int chm_get_program(chm_mgr_t *p_chm, /* Channel manager reference */ bband_t band, unsigned short prog_number, bapp_ch_t *p_ch ) { unsigned int section_size, pmt_section_size; TS_PMT_stream pmt; TS_PAT_program pat; int result = 0; int idx,p_idx; bapp_t *p_app = (bapp_t*)p_chm->p_app; bmessage_stream_params_init(&p_app->msg_params, p_app->msg); p_app->msg_params.band = band; p_app->msg_params.pid = (unsigned short)0; /* PAT */ section_size = PAT_BUF_LEN; if ((result = chm_getmessage(p_chm,p_chm->pat_buf,§ion_size, PAT_TIMEOUT)) != 0) { BDBG_WRN(("Error getting PAT 0x%04x = %d\n",prog_number, result)); return result; } if (TS_PAT_validate(p_chm->pat_buf, section_size) != true) { BDBG_WRN(("TS_PAT_validate failed 0x%04x\n",prog_number)); return eCHM_ERR_FAILED; } for (idx = 0; idx < TS_PAT_getNumPrograms(p_chm->pat_buf); idx++) { if (TS_PAT_getProgram( p_chm->pat_buf, section_size, idx, &pat ) != b_ok) { BDBG_WRN(("TS_PAT_validate failed 0x%04x\n",prog_number)); return eCHM_ERR_FAILED; } if (pat.program_number != prog_number) continue; bmessage_stream_params_init(&p_app->msg_params, p_app->msg); p_app->msg_params.band = band; p_app->msg_params.pid = (unsigned short)pat.PID; /* PMT */ p_app->msg_params.filter.coef[0] = 0x2; p_app->msg_params.filter.mask[0] = 0x00; p_app->msg_params.filter.excl[0] = 0xff; pmt_section_size = PMT_BUF_LEN; if ((result = chm_getmessage(p_chm,p_chm->pmt_buf,&pmt_section_size, PMT_TIMEOUT)) != 0) { BDBG_ERR(("Error getting PAT 0x%04x\n",prog_number)); return result; } p_ch->pcr_pid = TS_PMT_getPcrPid(p_chm->pmt_buf,pmt_section_size); for (p_idx = 0; p_idx < TS_PMT_getNumStreams(p_chm->pmt_buf,pmt_section_size); p_idx++) { TS_PSI_descriptor psi_desc,lang_desc; int strm_idx; if (TS_PMT_getStream( p_chm->pmt_buf, pmt_section_size, p_idx, &pmt ) != b_ok) { BDBG_WRN(("Failure processing PMT %d\n",p_idx)); return eCHM_ERR_FAILED; } strm_idx = 0; lang_desc = NULL; while ((psi_desc = TS_PMT_getStreamDescriptor(p_chm->pmt_buf,pmt_section_size, p_idx,strm_idx)) != NULL) { if (psi_desc[0] == TS_PSI_DT_ISO639Language) /* 0x0A ISO-639 Language descriptor */ { BDBG_WRN(("ISO-639 DESC\n" )); lang_desc = psi_desc; break; } strm_idx++; if (strm_idx > 0xFF) { BDBG_ERR(("PMT Stream Desc limit exceeded %d\n",strm_idx)); break; } if (chm_check_cancel(p_chm)) { return eCHM_ERR_CANCELED; } } if (pmt.stream_type == 0x02) /* Video */ { BDBG_WRN(("Video PID[%d] = 0x%04x\n",p_idx,pmt.elementary_PID )); if (!p_ch->video_pid) { p_ch->video_pid = pmt.elementary_PID; } #ifdef BQAM_SCRIPT } else if (pmt.stream_type == 0x80) /* digicipher */ { BDBG_WRN(("Video PID[%d] = 0x%04x\n",p_idx,pmt.elementary_PID )); if (!p_ch->video_pid) { p_ch->video_pid = pmt.elementary_PID; } #endif } else if (pmt.stream_type == 0x81) { BDBG_WRN(("Audio PID[%d] = 0x%04x\n",p_idx,pmt.elementary_PID )); if (!p_ch->audio_pid[0]) { p_ch->audio_pid[0] = pmt.elementary_PID; } } else { /* We only support the above 3 types of stream */ BDBG_WRN(("Unsupported stream type[0x%02x]\n", pmt.stream_type )); return eCHM_ERR_FAILED; } } break; } return result; } /* Summary: Scan for channels using PSI. Description: Scan for channels using PSI. Returns number of channels added. */ static int chm_scan_psi(chm_mgr_t *p_chm, /* Channel manager reference */ bband_t band, int freq_idx) { unsigned int section_size, pmt_section_size; TS_PMT_stream pmt; TS_PAT_program pat; int result = 0; int idx,p_idx,tresult, desc_idx; bapp_ch_t ch; bapp_t *p_app = (bapp_t*)p_chm->p_app; TS_PSI_descriptor psi_desc; unsigned short ca_pid = 0; /* Get the VCT */ bmessage_stream_params_init(&p_app->msg_params, p_app->msg); p_app->msg_params.band = band; p_app->msg_params.pid = (unsigned short)0; /* PAT */ section_size = PAT_BUF_LEN; if ((result = chm_getmessage(p_chm,p_chm->pat_buf,§ion_size, PAT_TIMEOUT)) != 0) { BDBG_WRN(("Timeout getting PAT\n")); return result; } if (TS_PAT_validate(p_chm->pat_buf, section_size) != true) { BDBG_WRN(("TS_PAT_validate failed\n")); return eCHM_ERR_FAILED; } for (idx = 0; idx < TS_PAT_getNumPrograms(p_chm->pat_buf); idx++) { if (chm_check_cancel(p_chm)) { return eCHM_ERR_CANCELED; } memset(&ch,0,sizeof(ch)); section_size = PAT_BUF_LEN; if (TS_PAT_getProgram( p_chm->pat_buf, section_size, idx, &pat ) != b_ok) { BDBG_WRN(("TS_PAT_validate failed 0x%04x\n",pat.PID)); return eCHM_ERR_FAILED; } bmessage_stream_params_init(&p_app->msg_params, p_app->msg); p_app->msg_params.band = band; p_app->msg_params.pid = (unsigned short)pat.PID; /* PMT */ p_app->msg_params.filter.coef[0] = 0x2; p_app->msg_params.filter.mask[0] = 0x00; p_app->msg_params.filter.excl[0] = 0xff; pmt_section_size = PMT_BUF_LEN; if ((tresult = chm_getmessage(p_chm,p_chm->pmt_buf,&pmt_section_size, PMT_TIMEOUT)) != 0) { BDBG_WRN(("Failed getting PMT 0x%04x\n",pat.PID)); continue; } ch.major = (freq_idx == 0x7F) ? 0 : freq_idx+2; //freq_idx is off by 2 ch.minor = idx + 1; ch.freq_idx = freq_idx; ch.pcr_pid = TS_PMT_getPcrPid(p_chm->pmt_buf,pmt_section_size); ca_pid = 0; #ifdef DTA_TEST ch.program_number = pat.program_number; #endif /* Get CA pid for cable */ desc_idx = 0; while ((psi_desc = TS_PMT_getDescriptor(p_chm->pmt_buf, pmt_section_size,desc_idx++)) != NULL) { if (desc_idx > 0xFF) { BDBG_ERR(("PMT Desc limit exceeded %d\n",desc_idx)); break; } if (chm_check_cancel(p_chm)) { return eCHM_ERR_CANCELED; } if (psi_desc[0] == 9) { ca_pid = (((unsigned short)(psi_desc[4] & 0x1F)) << 8) | (unsigned short)psi_desc[5]; BDBG_WRN(("CA PID = 0x%04x\n",ca_pid)); break; } } if (ca_pid != 0x0000) { BDBG_WRN(("Program has CA PID 0x%02x so skip it\n",ca_pid)); //continue; } for (p_idx = 0; p_idx < TS_PMT_getNumStreams(p_chm->pmt_buf,pmt_section_size); p_idx++) { if (TS_PMT_getStream( p_chm->pmt_buf, pmt_section_size, p_idx, &pmt ) != b_ok) { BDBG_WRN(("Failure processing PMT %d\n",p_idx)); return eCHM_ERR_FAILED; } if (pmt.stream_type == 0x02) /* Video */ { BDBG_MSG(("Video PID[%d] = 0x%04x\n",p_idx,pmt.elementary_PID )); if (!ch.video_pid) ch.video_pid = pmt.elementary_PID; } else if (pmt.stream_type == 0x81) { BDBG_MSG(("Audio PID[%d] = 0x%04x\n",p_idx,pmt.elementary_PID )); if (!ch.audio_pid[0]) ch.audio_pid[0] = pmt.elementary_PID; } #ifdef BQAM_SCRIPT else if ( pmt.stream_type == 0x80) //digicipher { BDBG_MSG(("Video PID[%d] = 0x%04x\n",p_idx,pmt.elementary_PID )); if (!ch.video_pid) ch.video_pid = pmt.elementary_PID; } } if ((ch.video_pid) || (ch.audio_pid[0])) { bool clear; unsigned short pid; if (ch.video_pid) pid = (unsigned short)ch.video_pid; else pid = (unsigned short)ch.audio_pid[0]; clear = chm_check_sc(p_chm, band, pid); if (clear) { ch.psi = 1; chm_add_channel(p_chm,&ch); result += 1; } } #else ch.psi = 1; chm_add_channel(p_chm,&ch); result += 1; } #endif } return result; } /* Summary: Scan for channels using VCT. Description: Scan for channels using VCT. Returns number of channels added. */ static int chm_scan_vct(chm_mgr_t *p_chm, /* Channel manager reference */ bband_t band, int freq_idx) { unsigned int section_size; int minor_idx; bool got_vct; unsigned char section_number; int result = 0; bapp_ch_t ch; bapp_t *p_app = (bapp_t*)p_chm->p_app; PSIP_VCT_channel vct; unsigned int timeout; bool have_program; char dst; int local_offset; timeout = bos_getticks() + MS_TO_TICKS(3 * VCT_TIMEOUT ); memset(&vct,0,sizeof(vct)); /* Get the VCT */ section_number = 0; do { bmessage_stream_params_init(&p_app->msg_params, p_app->msg); p_app->msg_params.band = band; p_app->msg_params.pid = 0x1ffb; /* VCT */ p_app->msg_params.filter.coef[0] = 0xc8; /* Table ID for terrestrial */ p_app->msg_params.filter.mask[0] = 0x01; p_app->msg_params.filter.excl[0] = 0xff; p_app->msg_params.filter.coef[6] = section_number; /* section number */ p_app->msg_params.filter.mask[6] = 0x00; p_app->msg_params.filter.excl[6] = 0xff; /* All PSIP tables have protocol_version which must be = 0 */ p_app->msg_params.filter.coef[8] = 0x00; /* protocol_version */ p_app->msg_params.filter.mask[8] = 0x00; p_app->msg_params.filter.excl[8] = 0xff; got_vct = false; do { section_size = VCT_BUF_LEN; if (chm_getmessage(p_chm,p_chm->vct_buf,§ion_size, VCT_TIMEOUT) != 0) { BDBG_WRN(("Timeout waiting for VCT!")); if (chm_check_cancel(p_chm)) { return eCHM_ERR_CANCELED; } return eCHM_ERR_TIMEOUT; } if (PSIP_VCT_getNumChannels(p_chm->vct_buf) == 0) { BDBG_WRN(("No VCT Found!")); break; } BDBG_MSG(("Adding PSIP_VCT_getNumChannels %d!",PSIP_VCT_getNumChannels(p_chm->vct_buf))); for (minor_idx = 0; minor_idx < PSIP_VCT_getNumChannels(p_chm->vct_buf); ++minor_idx) { int desc_idx; TS_PSI_descriptor psi_desc; got_vct = true; BDBG_MSG(("Get VCT[%d]\n",minor_idx)); have_program = false; if (PSIP_VCT_getChannel( (const uint8_t *)p_chm->vct_buf, minor_idx, &vct ) == BERR_SUCCESS) { if ((vct.hidden) || (vct.minor_channel_number == 0)) { BDBG_WRN(("Channel hidden or analog channel %d, %d",vct.hidden,vct.minor_channel_number)); continue; } memset(&ch,0,sizeof(ch)); ch.freq_idx = freq_idx; ch.minor = vct.minor_channel_number; ch.major = vct.major_channel_number; memcpy(ch.ch_name,vct.short_name,MAX_CH_NAME_CHARS * sizeof(unsigned short)); desc_idx = 0; while ((psi_desc = PSIP_VCT_getChannelDescriptor(p_chm->vct_buf, minor_idx,desc_idx)) != NULL) { if (psi_desc[0] == 0xA1) /* Service location descriptor */ { PSIP_SLD_header sld_header; PSIP_SLD_element sld_elem; int sld_idx,lidx,num_aud; PSIP_SLD_getHeader(psi_desc,&sld_header); have_program = true; ch.pcr_pid = sld_header.PCR_PID; BDBG_MSG(("SLD number_elements = %d\n",sld_header.number_elements )); BDBG_MSG(("PCR PID = 0x%04x\n",ch.pcr_pid )); num_aud = 0; for (sld_idx = 0; sld_idx < sld_header.number_elements; ++sld_idx) { if (PSIP_SLD_getElement(psi_desc,sld_idx,&sld_elem) != BERR_SUCCESS) { BDBG_WRN(("Error processing SLD Desc %d of %d",sld_idx,sld_header.number_elements)); break; } if (sld_elem.stream_type == 0x02) /* Video */ { BDBG_WRN(("Video PID[%d] = 0x%04x\n",sld_idx,sld_elem.elementary_PID )); if (!ch.video_pid) { ch.video_pid = sld_elem.elementary_PID; } #ifdef BQAM_SCRIPT } else if (sld_elem.stream_type == 0x80) /* digicipher */ { BDBG_WRN(("Video PID[%d] = 0x%04x\n",sld_idx,sld_elem.elementary_PID )); if (!ch.video_pid) { ch.video_pid = sld_elem.elementary_PID; } #endif } else if (sld_elem.stream_type == 0x81) { BDBG_MSG(("Audio PID[%d] = 0x%04x, lang => %c%c%c\n",sld_idx,sld_elem.elementary_PID, sld_elem.ISO_639_language_code[0],sld_elem.ISO_639_language_code[1],sld_elem.ISO_639_language_code[2] )); num_aud++; if ((lidx = chm_valid_lang(p_chm,sld_elem.ISO_639_language_code)) >= 0 ) { if (ch.audio_pid[lidx] != 0) { BDBG_WRN(("Duplicate language Audio PID[%d] = 0x%04x, lang => %c%c%c\n",sld_idx,sld_elem.elementary_PID, sld_elem.ISO_639_language_code[0],sld_elem.ISO_639_language_code[1],sld_elem.ISO_639_language_code[2] )); } else { ch.audio_pid[lidx] = sld_elem.elementary_PID; ch.has_lang |= 1 << lidx; } } else { BDBG_WRN(("Throwing out Audio PID[%d] = 0x%04x, lang => %c%c%c\n",sld_idx,sld_elem.elementary_PID, sld_elem.ISO_639_language_code[0],sld_elem.ISO_639_language_code[1],sld_elem.ISO_639_language_code[2] )); } } } /* Backup plan if there is no valid language code */ if ((ch.audio_pid[0] == 0) && (ch.audio_pid[1] == 0) && (ch.audio_pid[2] == 0) && num_aud) { BDBG_WRN(("No valid audio PID found for languages in audio %d descs...\n",num_aud)); num_aud = 0; for (sld_idx = 0; sld_idx < sld_header.number_elements; ++sld_idx) { if (PSIP_SLD_getElement(psi_desc,sld_idx,&sld_elem) != BERR_SUCCESS) { BDBG_WRN(("Error processing SLD Desc %d of %d",sld_idx,sld_header.number_elements)); break; } if (sld_elem.stream_type == 0x81) { BDBG_WRN(("Audio PID[%d] = 0x%04x, lang => %c%c%c\n",sld_idx,sld_elem.elementary_PID, sld_elem.ISO_639_language_code[0],sld_elem.ISO_639_language_code[1],sld_elem.ISO_639_language_code[2] )); ch.has_lang |= 1 << num_aud; ch.audio_pid[num_aud++] = sld_elem.elementary_PID; } } } } desc_idx++; if (desc_idx > 0xFF) { BDBG_ERR(("VCT Channel Desc limit exceeded %d\n",desc_idx)); break; } if (chm_check_cancel(p_chm)) { return eCHM_ERR_CANCELED; } } if (!have_program) { if (chm_get_program(p_chm,band,vct.program_number,&ch) == 0) { BDBG_MSG(("program V: 0x%04x, A: 0x%04x, PCR: 0x%04x\n",ch.video_pid,ch.audio_pid,ch.pcr_pid)); have_program = true; } } if (p_chm->local_time_source == eTS_NONE) { if (p_app->settings.time_zone != 0) { p_chm->local_time_source = eTS_FIXED; p_chm->local_offset = 0 - (int)g_tz_map[p_app->settings.time_zone]; p_app->chm.local_offset *= 3600; p_chm->local_dst_obs = p_app->settings.dst; BDBG_WRN(("### Local time %d Fixed ###\n",p_chm->local_offset)); } /* set local time offset */ else if ((tsid_to_utc_offset(vct.channel_TSID,&local_offset,&dst) == 0)) { p_chm->local_time_source = eTS_TSID; p_chm->local_offset = local_offset; p_chm->local_dst_obs = dst; BDBG_WRN(("### Local time %d from TSID 0x%04x ###\n",local_offset,vct.channel_TSID)); } } if (have_program) { chm_add_channel(p_chm,&ch); result += 1; } } else { BDBG_WRN(("PSIP_VCT_getChannel %d failed",minor_idx)); } } }while ((section_size != 0) && !got_vct); if (chm_check_cancel(p_chm)) { return eCHM_ERR_CANCELED; } section_number++; }while ((section_number <= p_chm->vct_buf[7]) && (timeout < bos_getticks())); return((result > 0) || (timeout < bos_getticks())) ? result : eCHM_ERR_TIMEOUT; } /* Summary: Clear the current channel info. */ static void chm_make_current(chm_mgr_t *p_chm, chm_info_t *p_info) { /* Clear current info */ if (bos_acquire_mutex(&p_chm->mutex,CHM_MUTEX_TIMEOUT) == b_ok) { memcpy(&p_chm->cur_info,p_info,sizeof(chm_info_t)); bos_release_mutex(&p_chm->mutex); } } /* Summary: Clear the current channel info. */ static int chm_clear_current(chm_mgr_t *p_chm) { bapp_t *p_app = (bapp_t*)p_chm->p_app; /* Clear current info */ if (bos_acquire_mutex(&p_chm->mutex,CHM_MUTEX_TIMEOUT) == b_ok) { memset(&p_chm->cur_info,0,sizeof(chm_info_t)); p_app->prog_rating_str[0] = 0; p_app->psip_rating_str[0] = 0; BDBG_MSG(("%s reset rating string @ %d\n",__FUNCTION__,__LINE__)); p_app->tv_rating = 0; p_chm->got_stt = false; bos_release_mutex(&p_chm->mutex); } return 0; } /* Summary: process the MMS string and associated tables. Returns non-zero on failure. */ int chm_process_mss(chm_mgr_t *p_chm, /* Channel manager reference */ PSIP_MSS_string p_mms, /* Pointer to MMS */ unsigned char* mms_buf, /* string buffer */ unsigned int mms_len /* string buffer length */ ) { int str_idx,lsize; char *p_code; BERR_Code retcode; int num_str; bapp_t *p_app; if (!p_mms) return eCHM_ERR_FAILED; num_str = PSIP_MSS_getNumStrings(p_mms); p_app = (bapp_t*)p_chm->p_app; memset(mms_buf,0,mms_len); for (str_idx = 0; str_idx < num_str; ++str_idx) { if ((retcode = PSIP_MSS_getCode(p_mms,str_idx, &p_code)) != BERR_SUCCESS) { BDBG_WRN(("PSIP_MSS_getCode:%d failed %d\n",__LINE__,retcode)); memset(mms_buf,0,mms_len); continue; } lsize = mms_len; if (chm_valid_lang(p_chm,p_code) != p_app->lang) { #ifdef BCM_DEBUG if ((retcode = PSIP_MSS_getString(p_mms,str_idx,&lsize, mms_buf)) != BERR_SUCCESS) { BDBG_WRN(("PSIP_MSS_getCode:%d failed %d\n",__LINE__,retcode)); } if (lsize > 0) { BDBG_MSG(("Lang mismatch %c%c%c %s\n",p_code[0],p_code[1],p_code[2],mms_buf)); } memset(mms_buf,0,mms_len); #endif continue; } if ((retcode = PSIP_MSS_getString(p_mms,str_idx,&lsize, mms_buf)) != BERR_SUCCESS) { BDBG_ERR(("MSS invalid %d\n",retcode)); memset(mms_buf,0,mms_len); } BDBG_MSG(("%s:%d idx = %d, %d/%d-%s\n",__FUNCTION__,__LINE__,str_idx,lsize,mms_len,mms_buf)); return 0; } /* in case no valid language exists just use first string */ if (num_str > 0) { lsize = mms_len; if ((retcode = PSIP_MSS_getString(p_mms,0,&lsize, mms_buf)) == BERR_SUCCESS) { BDBG_WRN(("No language match using default\n")); return eCHM_ERR_OK; } else { BDBG_WRN(("PSIP_MSS_getString failed %d\n",retcode)); } } BDBG_WRN(("%s:%d no valid string found, num_str = %d\n",__FUNCTION__,__LINE__,num_str)); return eCHM_ERR_FAILED; } /* Summary: process the VCT . Returns non-zero on failure. */ static int chm_process_vct(chm_mgr_t *p_chm, /* Channel manager reference */ unsigned char *msg_buf, /* Buffer containing the MGT table */ unsigned int msg_size, /* PSI buffer size */ chm_info_t *p_info, /* info structure to populate */ unsigned short minor /* minor ch number */ ) { int minor_idx,sld_idx,local_offset; TS_PSI_descriptor psi_desc; PSIP_VCT_channel vct; char dst; bapp_t *p_app = (bapp_t*)p_chm->p_app; memset(&vct,0,sizeof(vct)); if (PSIP_VCT_getNumChannels(msg_buf) == 0) return eCHM_ERR_FAILED; for (minor_idx = 0; minor_idx < PSIP_VCT_getNumChannels(msg_buf); ++minor_idx) { if (chm_check_cancel(p_chm)) return eCHM_ERR_CANCELED; if (PSIP_VCT_getChannel( (const uint8_t *)msg_buf, minor_idx, &vct ) != BERR_SUCCESS) { BDBG_WRN(("Failure processing VCT %d\n",minor_idx)); return eCHM_ERR_FAILED; } if (vct.minor_channel_number != minor) { BDBG_WRN(("VCT for %d not %d\n",vct.minor_channel_number, minor)); memset(&vct,0,sizeof(vct)); continue; } p_info->major = vct.major_channel_number; p_info->minor = vct.minor_channel_number; p_info->source_id = vct.source_id; p_info->program_number = vct.program_number; memcpy(p_info->ch_name, vct.short_name,sizeof(uint16_t) * MAX_CH_NAME_CHARS), p_info->got_vct = true; if (p_chm->local_time_source == eTS_NONE) { if (p_app->settings.time_zone != 0) { p_chm->local_time_source = eTS_FIXED; p_chm->local_offset = 0 - (int)g_tz_map[p_app->settings.time_zone]; p_app->chm.local_offset *= 3600; p_chm->local_dst_obs = p_app->settings.dst; BDBG_WRN(("### Local time %d Fixed ###\n",p_chm->local_offset)); } /* set local time offset */ else if ((tsid_to_utc_offset(vct.channel_TSID,&local_offset,&dst) == 0)) { p_chm->local_time_source = eTS_TSID; p_chm->local_offset = local_offset; p_chm->local_dst_obs = dst; BDBG_WRN(("### Local time %d from TSID 0x%04x ###\n",local_offset,vct.channel_TSID)); } } sld_idx = 0; while ((psi_desc = PSIP_VCT_getChannelDescriptor(msg_buf, minor_idx,sld_idx)) != NULL) { if (psi_desc[0] == 0xA0) /* Extended channel name descriptor */ { PSIP_MSS_string lstr = PSIP_ECND_getLongChannelName(psi_desc); chm_process_mss(p_chm,lstr,p_info->ext_ch_name,MAX_LONG_NAME_CHARS); } sld_idx++; } return eCHM_ERR_OK; } return eCHM_ERR_FAILED; /* channel not found */ } /* Summary: Get channel info details for tuning. Description: Get channel info details for tuning. Returns non-zero on failure. */ static int chm_get_prog(chm_mgr_t *p_chm, /* Channel manager reference */ bapp_ch_t *p_ch, chm_info_t *p_info /* info structure to populate */ ) { unsigned int section_size; int result = eCHM_ERR_FAILED; unsigned char section_number; bapp_t *p_app = (bapp_t*)p_chm->p_app; section_number = 0; do { /* Get the VCT */ bmessage_stream_params_init(&p_app->msg_params, p_app->msg); p_app->msg_params.band = p_app->band; p_app->msg_params.pid = 0x1ffb; /* VCT */ section_size = VCT_BUF_LEN; p_app->msg_params.filter.coef[0] = 0xc8; /* Table ID for terrestrial */ p_app->msg_params.filter.mask[0] = 0x01; p_app->msg_params.filter.excl[0] = 0xff; p_app->msg_params.filter.coef[6] = section_number; /* section number */ p_app->msg_params.filter.mask[6] = 0x00; p_app->msg_params.filter.excl[6] = 0xff; /* All PSIP tables have protocol_version which must be = 0 */ p_app->msg_params.filter.coef[8] = 0x00; /* protocol_version */ p_app->msg_params.filter.mask[8] = 0x00; p_app->msg_params.filter.excl[8] = 0xff; if (chm_getmessage(p_chm,p_chm->vct_buf,§ion_size, VCT_TIMEOUT) != 0) { BDBG_WRN(("%s timeout waiting for VCT (section # %d)\n",__FUNCTION__,section_number)); return result; } if (chm_check_cancel(p_chm)) return eCHM_ERR_CANCELED; result = chm_process_vct(p_chm,p_chm->vct_buf, section_size, p_info,p_app->settings.ch[p_chm->cur_ch_num].minor); if (result != 0) { BDBG_WRN(("VCT entry not found %d\n",section_number)); section_number++; continue; } if ((result = chm_get_program(p_chm,p_app->band,p_info->program_number,p_ch)) != 0) { BDBG_WRN(("Failure processing PMT, %d\n",p_info->program_number )); return result; } result = 0; break; }while (section_size != 0); return result; } /* Summary: Generic tuner cancel callback. */ void chm_tune_cancel_callback(void *context) { bapp_t *p_app = ( bapp_t *)context; if (chm_check_cancel(&p_app->chm)) { BDBG_WRN(("### CANCEL TUNE ###\n")); p_app->ds.cancel = true; } } #if 1 /* * Scan command history for best command value. */ static int chm_check_hist(chm_mgr_t *p_chm, unsigned char *best) { int idx,max,max_idx; max = 0; max_idx = -1; for (idx = 0; idx < MAX_CMD_HIST; ++idx) { if (!p_chm->cmd_hist[idx].locked) continue; if (p_chm->cmd_hist[idx].snr > max) { max = p_chm->cmd_hist[idx].snr; max_idx = idx; } } if (max_idx >= 0) { *best = p_chm->cmd_hist[max_idx].cmd; return 0; } return -1; } /* Summary: Find the best smart antenna setting, assumes tuner is tuned. Return non-zero on failure to find a setting. */ #define MAX_SMART_STATUS 8 static int chm_config_smart(chm_mgr_t *p_chm, /* Channel manager reference */ int idx) { uint8_t coarse_dir,dir,polarity,preamp_gain,cmd; uint8_t num_dir,num_coarse, num_polarity,num_preamp_gain; uint32_t freq = (unsigned int)s_ch_to_freq[0]; bapp_t *p_app = (bapp_t*)p_chm->p_app; btuner_ds_status ds_status; if ((p_app->ch_scan_type == eQUICK_SCAN) || (p_app->ch_scan_type == eQUICK_RESCAN)) { num_dir = num_polarity = num_preamp_gain = 1; num_coarse = 5; } else { num_dir = 4; num_polarity = 1; num_preamp_gain = 4; num_coarse = 4; } /* Reset history */ memset(p_chm->cmd_hist,0,sizeof(cmd_hist_t) * MAX_CMD_HIST); /* Do smart scan */ for (coarse_dir = 0; coarse_dir < num_coarse; ++coarse_dir) { for (dir = 0; dir < num_dir; ++dir) { for (polarity = 0; polarity < num_polarity; ++polarity) { for (preamp_gain = 0; preamp_gain < num_preamp_gain; ++preamp_gain) { if (coarse_dir==4) { /* only gets here for eQUICK_SCAN only, for 5th face of smart antenna */ cmd = (0x2 << 5); cmd |= (0x2 << 3); } else { cmd = (coarse_dir << 5); cmd |= (dir << 3); } cmd |= (polarity << 2); cmd |= preamp_gain; bant_send(cmd,idx + 2); /* Check to see if scan was canceled */ if (chm_check_cancel(p_chm)) return -1; bos_sleep(25); freq = (unsigned int)s_ch_to_freq[idx]; freq *= 1000000; /* convert to Hz */ btuner_ds_params_init(&p_app->ds, p_app->tuner); p_app->ds.timeout = 16; /* 50ms * 16 = 800ms timeout */ p_app->ds.cancel_callback = chm_tune_cancel_callback; p_app->ds.cancel_callback_context = p_app; p_app->ds.wait_for_lock = true; #ifdef BQAM_SCRIPT p_app->ds.fecMode = eFEC_ANNEX_B; p_app->ds.qamMode = eQAM_Scan; #endif if (btuner_tune_ds(p_app->tuner, freq, &p_app->ds) < 0) { continue; } else { btuner_get_ds_status(p_app->tuner, &ds_status); if (!ds_status.lock || (ds_status.snr > 40)) { int tidx; BDBG_WRN(("strange SNR try again(%d,%d, cmd = 0x%02x,snr = %d)\n",idx,freq,cmd,ds_status.snr)); for (tidx = 0; tidx < 50; ++tidx) { bos_sleep(20); btuner_get_ds_status(p_app->tuner, &ds_status); if (ds_status.lock && (ds_status.snr > 14) && (ds_status.snr < 40)) { break; } /* Check to see if scan was canceled */ if (chm_check_cancel(p_chm)) return -1; } if (!ds_status.lock || (ds_status.snr <= 14) || (ds_status.snr >= 40)) { BDBG_WRN(("Could not re-lock(%d,%d, cmd = 0x%02x,snr = %d)\n",idx,freq,cmd,ds_status.snr)); continue; } } p_chm->cmd_hist[cmd].cmd = cmd; p_chm->cmd_hist[cmd].locked = ds_status.lock; p_chm->cmd_hist[cmd].snr = ds_status.snr; BDBG_WRN(("%s (%d,%d, cmd = 0x%02x,snr = %d)\n",__FUNCTION__,idx,freq,cmd,ds_status.snr)); break; } } /* preamp_gain */ } /* polarity */ } /* dir */ } /* course_dir */ /* find best command value based on SNR value */ if (chm_check_hist(p_chm,&cmd) == 0) { BDBG_WRN(("%s best cmd(%d,%d, cmd = 0x%02x)\n",__FUNCTION__,idx,freq,cmd)); p_chm->ant_cmd = cmd; /* Check to see if scan was canceled */ if (chm_check_cancel(p_chm)) return -1; /* Send the command to put antenna in the best state */ bant_send(cmd,idx + 2); bos_sleep(25); return 0; } else { p_chm->ant_cmd = 0xFF; BDBG_WRN(("%s none(%d,%d, cmd = 0x%02x)\n",__FUNCTION__,idx,freq,p_chm->ant_cmd)); } return -1; } /* Summary: Scan for channels to create a channel map. Description: Scan for all available channels. */ static void chm_stream_close(chm_mgr_t *p_chm /* Channel manager reference */ ) { bapp_t *p_app = (bapp_t*)p_chm->p_app; if (p_app->stream) { if (p_chm->stt_msg != NULL) { if(b_ok != smessage_stop(p_chm->stt_msg)){ BDBG_ERR(("%s:%d",__FILE__, __LINE__)); } smessage_close(p_chm->stt_msg); p_chm->stt_msg = NULL; } if (p_chm->rrt_msg != NULL) { if(b_ok != smessage_stop(p_chm->rrt_msg)){ BDBG_ERR(("%s:%d",__FILE__, __LINE__)); } smessage_close(p_chm->rrt_msg); p_chm->rrt_msg = NULL; } bstream_close(p_app->stream); p_app->stream = 0; } } /* Summary: Is frequency in the list of channels. */ static bool chm_find_freq_idx(chm_mgr_t *p_chm, /* Channel manager reference */ unsigned char freq_idx) { int ch_idx; bapp_t *p_app = (bapp_t*)p_chm->p_app; for (ch_idx = 0; ch_idx < p_app->settings.num_channels; ch_idx++) { if (p_app->settings.ch[ch_idx].freq_idx == freq_idx) { return true; } } return false; } /* Summary: Find list of frequencies to scan. */ static void chm_create_freq_list(chm_mgr_t *p_chm /* Channel manager reference */ ) { int freq_idx; s_freq_cnt = 0; for (freq_idx = 0; freq_idx < MAX_MAJOR_CH; freq_idx++) { if (!chm_find_freq_idx(p_chm,freq_idx)) { s_freq_list[s_freq_cnt++] = freq_idx; } } } /* Summary: Scan for channels to create a channel map. Description: Scan for all available channels. */ static void chm_scan(chm_mgr_t *p_chm /* Channel manager reference */ ) { unsigned int freq; int idx,num,list_idx; bapp_t *p_app = (bapp_t*)p_chm->p_app; chm_stop_decode(p_chm); if (p_app->ch_scan_type < eFULL_RESCAN) { /* Completely reset and start over */ p_app->settings.num_channels = 0; } memset(p_chm->p_info,0,p_chm->max_info_num * sizeof(chm_info_t)); chm_create_freq_list(p_chm); p_chm->cur_ch_num = 0xFF; chm_stream_close(p_chm); /* Add streamer channel */ #ifdef BCM_DEBUG #if (BCHP_CHIP != 3543) p_app->band = bstreamer_attach(0,bstream_mpeg_type_ts); BDBG_MSG(("chm_scan calling bstream_open for streamer input\n")); p_app->stream = bstream_open(p_app->band, &p_app->mpeg); if (p_app->stream) { BDBG_MSG(("STREAMER INPUT chm_scan_vct\n")); if ((num = chm_scan_vct(p_chm,p_app->band,0x7F)) <= 0) { BDBG_MSG(("chm_scan_vct found no channels\n")); BDBG_MSG(("STREAMER INPUT GET PSI\n")); if ((num = chm_scan_psi(p_chm,p_app->band,0x7F)) <= 0) { BDBG_MSG(("chm_scan_psi found no channels\n")); } else { BDBG_MSG(("STREAMER INPUT PSI channels %d \n", num)); } } else { BDBG_MSG(("STREAMER INPUT VCT channels %d \n", num)); } chm_stream_close(p_chm); } #endif #endif for (list_idx = 0; list_idx < s_freq_cnt; list_idx++) { idx = s_freq_list[list_idx]; freq = (unsigned int)s_ch_to_freq[idx]; freq *= 1000000; /* convert to Hz */ BDBG_WRN(("Scan[%d] freq = %d\n",idx,freq)); /* Check to see if scan was canceled */ if (chm_check_cancel(p_chm)) break; /* output progress event id will be % done */ p_chm->chm_evt.type = eCHM_PROGRESS; p_chm->chm_evt.id = (idx * 100)/MAX_MAJOR_CH; chm_post_app_event(p_chm,&p_chm->chm_evt); /* Try tuning */ BDBG_WRN(("Start scan type = %d\n",p_app->ch_scan_type)); #ifndef BQAM_SCRIPT if (bant_detect()) { if (chm_config_smart(p_chm,idx) != 0) { continue; } } else { p_chm->ant_cmd = 0xFF; } #endif btuner_ds_params_init(&p_app->ds, p_app->tuner); p_app->ds.cancel_callback = chm_tune_cancel_callback; p_app->ds.cancel_callback_context = p_app; p_app->ds.wait_for_lock = true; #ifdef BQAM_SCRIPT p_app->ds.fecMode = eFEC_ANNEX_B; p_app->ds.qamMode = eQAM_Scan; #endif if (btuner_tune_ds(p_app->tuner, freq, &p_app->ds) < 0) { BDBG_WRN(("DS[%d] freq = %d not locked, try next freq\n",idx,freq)); chm_send_status(p_chm,freq,false); continue; } else { BDBG_WRN(("btuner_tune_ds[%d] freq = %d, success\n",idx,freq)); chm_send_status(p_chm,freq,true); } p_app->band = TUNER_BAND; BDBG_MSG(("chm_scan calling bstream_open\n")); p_app->stream = bstream_open(p_app->band, &p_app->mpeg); if (!p_app->stream) { BDBG_WRN(("bstream_open failed\n")); continue; } #ifndef BQAM_SCRIPT if ((num = chm_scan_vct(p_chm,p_app->band,idx)) <= 0) { BDBG_WRN(("chm_scan_vct found no channels\n")); #endif if ((num = chm_scan_psi(p_chm,p_app->band,idx)) <= 0) { BDBG_WRN(("chm_scan_psi found no channels\n")); } else { BDBG_MSG(("PSI channels %d \n", num)); } continue; #ifndef BQAM_SCRIPT } else { BDBG_MSG(("VCT channels %d \n", num)); } #endif } p_chm->rrt_size = 0; p_chm->chm_evt.type = eCHM_PROGRESS; p_chm->chm_evt.id = 100; chm_post_app_event(p_chm,&p_chm->chm_evt); /* leave scan state */ p_chm->cmd = eCHM_CANCEL; p_chm->cur_ch_num = p_app->cur_ch_num = 0; WriteReg32(SCRATCH_CMD_BASE,eIR_CHMAP); /* Output channel map */ } #else /* Summary: Scan for channels to create a channel map. Description: Scan for all available channels. */ static void chm_scan(chm_mgr_t *p_chm /* Channel manager reference */ ) { unsigned int freq; int idx; bapp_t *p_app = (bapp_t*)p_chm->p_app; chm_stop_decode(p_chm); /* Completely reset and start over */ p_app->settings.num_channels = 0; memset(p_chm->p_info,0,p_chm->max_info_num * sizeof(chm_info_t)); p_chm->cur_ch_num = 0xFF; chm_stream_close(p_chm); /* Add streamer channel */ #ifdef BCM_DEBUG p_app->band = bstreamer_attach(0,bstream_mpeg_type_ts); BDBG_MSG(("chm_scan calling bstream_open for streamer input\n")); p_app->stream = bstream_open(p_app->band, &p_app->mpeg); if (p_app->stream) { int num; BDBG_MSG(("STREAMER INPUT chm_scan_vct\n")); if ((num = chm_scan_vct(p_chm,p_app->band,0x7F)) == 0) { BDBG_MSG(("chm_scan_vct found no channels\n")); BDBG_MSG(("STREAMER INPUT GET PSI\n")); if ((num = chm_scan_psi(p_chm,p_app->band,0x7F)) == 0) { BDBG_MSG(("chm_scan_psi found no channels\n")); } else { BDBG_MSG(("STREAMER INPUT PSI channels %d \n", num)); } } else { BDBG_MSG(("STREAMER INPUT VCT channels %d \n", num)); } chm_stream_close(p_chm); } #endif for (idx = 0; idx < MAX_MAJOR_CH; ++idx) { freq = (unsigned int)s_ch_to_freq[idx]; freq *= 1000000; /* convert to Hz */ /* Check to see if scan was canceled */ if (chm_check_cancel(p_chm)) break; /* output progress event id will be % done */ p_chm->chm_evt.type = eCHM_PROGRESS; p_chm->chm_evt.id = (idx * 100)/MAX_MAJOR_CH; chm_post_app_event(p_chm,&p_chm->chm_evt); /* Try tuning */ btuner_ds_params_init(&p_app->ds, p_app->tuner); p_app->ds.cancel_callback = chm_tune_cancel_callback; p_app->ds.cancel_callback_context = p_app; p_app->ds.wait_for_lock = true; #ifdef BQAM_SCRIPT p_app->ds.fecMode = eFEC_ANNEX_B; p_app->ds.qamMode = eQAM_Scan; #endif p_app->band = btuner_tune_ds(p_app->tuner, freq, &p_app->ds); if (p_app->band < 0) { BDBG_WRN(("Tune failed (freq[%d] = %d)\n",idx, freq)); chm_send_status(p_chm,freq,false); continue; } chm_send_status(p_chm,freq,true); BDBG_MSG(("chm_scan calling bstream_open\n")); p_app->stream = bstream_open(p_app->band, &p_app->mpeg); if (!p_app->stream) { BDBG_WRN(("bstream_open failed\n")); continue; } if (chm_scan_vct(p_chm,p_app->band,idx) == 0) { BDBG_WRN(("chm_scan_vct found no channels\n")); continue; } } p_chm->chm_evt.type = eCHM_PROGRESS; p_chm->chm_evt.id = 100; chm_post_app_event(p_chm,&p_chm->chm_evt); /* leave scan state */ p_chm->cmd = eCHM_CANCEL; } #endif /* Summary: Post an event the app queue to provide status. */ static int chm_post_app_event(chm_mgr_t *p_chm, /* Channel manager reference */ chm_event_t *p_event /* Event to post to app queue */ ) { bapp_t *p_app = (bapp_t*)p_chm->p_app; bos_post_event(p_app->msg_queue,(bapp_task_event_t*)p_event); return 0; } /* Summary: Output the current RRT text */ #ifdef DEBUG_RRT static void chm_output_rrt(chm_mgr_t *p_chm) /* Channel manager reference */ { PSIP_RRT_header header; PSIP_RRT_dimension dim; PSIP_RRT_value val; int dim_idx,val_idx; char tmp_str[64]; bapp_t *p_app = (bapp_t*)p_chm->p_app; if (p_chm->rrt_size <= 0) return; PSIP_RRT_getHeader(p_app->settings.rrt, &header); if (chm_process_mss(p_chm,header.p_rating_region_name_text,tmp_str,64) == 0) { BDBG_WRN(("RRT %8s\n",tmp_str)); } for (dim_idx = 0; dim_idx < header.dimensions_defined; ++dim_idx) { if (PSIP_RRT_getDimension(p_app->settings.rrt,dim_idx,&dim) != BERR_SUCCESS) continue; if (chm_process_mss(p_chm,dim.p_dimension_name_text,tmp_str,sizeof(tmp_str)) == 0) { BDBG_WRN((" RRT[%d] %8s(%d)\n",dim_idx,tmp_str,dim.graduated_scale)); } for (val_idx = 0; val_idx < dim.values_defined; ++val_idx) { if (PSIP_RRT_getValue(p_app->settings.rrt,dim_idx,val_idx,&val) != BERR_SUCCESS) continue; if (chm_process_mss(p_chm,val.p_rating_value_text,tmp_str,sizeof(tmp_str)) == 0) { BDBG_WRN((" RRT[%d,%d] %8s\n",dim_idx,val_idx,tmp_str)); } if (chm_process_mss(p_chm,val.p_abbrev_rating_value_text,tmp_str,sizeof(tmp_str)) == 0) { BDBG_WRN((" RRT[%d,%d] %8s\n",dim_idx,val_idx,tmp_str)); } } } } #endif #define CHM_RRT_BUF_SIZE 0x1000 static uint8_t chm_rrt_buffer[CHM_RRT_BUF_SIZE]; static void *chm_smessage_rrt_callback(void * context, size_t data_size) { static chm_event_t chm_evt; PSIP_RRT_header header; int flags; chm_mgr_t *p_chm; bapp_t *p_app; p_chm = (chm_mgr_t*)context; p_app = (bapp_t*)p_chm->p_app; PSIP_RRT_getHeader(chm_rrt_buffer, &header); if ((header.rating_region == 1) || (memcmp(chm_rrt_buffer, p_app->settings.rrt, data_size) == 0)){ BDBG_WRN(("RRTs region 1 or they match so don't update\n")); p_chm->last_rrt += 2 * 60; return(void*)(chm_rrt_buffer); } flags = bos_enter_critical(); p_chm->last_rrt += 2 * 60; /* Make sure the app is not in ratings screen */ if ((p_app->screen_id != eSCREEN_RATINGS_RRT_DIM) && (p_app->screen_id != eSCREEN_RATINGS_RRT_VAL)) { int i; memcpy(p_app->settings.rrt, chm_rrt_buffer, data_size); /* clear current user rrt restriction settings */ for(i = 0; i < MAX_RRT_DIM; i++){ p_app->settings.ratings_rrt[i] = 0; } p_app->settings_dirty = true; } bos_exit_critical(flags); #ifdef DEBUG_RRT chm_output_rrt(p_chm); /* DEBUG ONLY */ #endif chm_evt.type = eCHM_EVT_RRT; chm_evt.id = ((((unsigned int)header.rating_region) << 8) | (((unsigned int)header.version_number))); chm_evt.ticks = bos_getticks(); bos_post_event(p_app->msg_queue,(bapp_task_event_t*)&chm_evt); return(void*)(chm_rrt_buffer); } /* Summary: Get the RRT. return non-zero on failure */ static int chm_get_rrt(chm_mgr_t *p_chm /* Channel manager reference */ ) { int cerr; smessage_stream_params_t params; bapp_t *p_app = (bapp_t*)p_chm->p_app; cerr = eCHM_ERR_OK; if (p_chm->rrt_msg != NULL) { if(b_ok != smessage_stop(p_chm->rrt_msg)){ BDBG_ERR(("%s:%d",__FILE__, __LINE__)); cerr = eCHM_ERR_FAILED; goto ExitFunc; } smessage_close(p_chm->rrt_msg); p_chm->rrt_msg = NULL; } p_chm->rrt_msg = smessage_open(bmessage_format_psi); if(NULL == p_chm->rrt_msg){ BDBG_ERR(("%s:%d",__FILE__, __LINE__)); cerr = eCHM_ERR_FAILED; goto ExitFunc; } smessage_stream_params_init(¶ms, p_chm->rrt_msg); params.band = p_app->band; params.pid = (uint16_t)0x1FFB; params.filter.coef[0] = 0xCA; params.filter.mask[0] = 0x00; params.filter.coef[8] = 0x00; params.filter.mask[8] = 0x00; params.buffer = chm_rrt_buffer; params.buffer_size = CHM_RRT_BUF_SIZE; params.data_ready_callback = chm_smessage_rrt_callback; params.overflow = chm_smessage_overflow; params.callback_context = (void *)p_chm; if(b_ok != smessage_start(¶ms, p_chm->rrt_msg)){ BDBG_ERR(("%s:%d",__FILE__, __LINE__)); cerr = eCHM_ERR_FAILED; } ExitFunc: return cerr; } #define BAPP_MPAA_RATING_STR_NUM 8 static const char *bapp_mpa_rating_str[] = { "", /* unrated */ "G", "PG", "PG-13", "R", "NC-17", "X", "NR" /* Not Rated */ }; #define BAPP_TV_RATING_STR_NUM 8 static const char *bapp_tv_rating_str[BAPP_TV_RATING_STR_NUM] = { "", /* unrated */ "TV-Y", "TV-Y7", "TV-G", "TV-PG", "TV-14", "TV-MA", "" /* unrated */ }; /* Summary: Use dimension and value to make a ratings decision based on the static ratings configuration in the UI. This function assumes region matches currently received RRT or current region == 1 (USA). */ static int chm_psip_block(bapp_t *p_app, unsigned char region,unsigned char dim,unsigned char val, char *rating_str) { int blocked = 0; int pos = 0; BDBG_MSG(("%s - region:%d\n", __FUNCTION__, region)); /* handle RRT ratings */ if ((p_app->settings.rrt_status == eRRT_AVAILABLE) && (region != 1)) { if (p_app->settings.ratings_rrt[dim] & (1 << val)) blocked |= 1; } BDBG_WRN(("RRT psip rating check - rrt dim:%d rrt val:%d blocked:%d\n", dim, val, blocked)); if ((region == 1) && (!blocked))/* US ratings are fixed */ { BDBG_WRN(("%s (movie:%x tv:%x) dim:%d val:%d\n",__FUNCTION__,p_app->settings.ratings_movies,p_app->settings.ratings_tv,dim,val)); if (val == 0) { BDBG_WRN(("US PSIP rating value = 0 so assume unblocked!\n")); return 0; } if (dim == 7) /* check settings for MPAA ratings */ { pos = 0; /* map to xds values */ if (val > 0) val--; p_app->movie_rating = val; pos = screen_ratings_movie_xds2pos(p_app->movie_rating); /* update rating string */ if (rating_str) { if (val < BAPP_MPAA_RATING_STR_NUM) strcpy(rating_str, bapp_mpa_rating_str[val]); } if (pos >= 0) blocked |= ((p_app->settings.ratings_movies >> pos) & (0 | (0x1))); } else /* check settings for TV ratings */ { switch (dim) { case 0: /* entire audience */ { int pos = 0; /* map to xds values */ if (val <= 1) { /* none (1) mapped to unrated */ p_app->tv_rating = 0; } else { /* TV-G, TV-PG, TV-14, and TV-MA map to corresponding XDS TV ratings */ p_app->tv_rating = val + 1; } pos = screen_ratings_tv_xds2pos(p_app->tv_rating); if (rating_str) { if (p_app->tv_rating < BAPP_TV_RATING_STR_NUM) strcpy(rating_str, bapp_tv_rating_str[p_app->tv_rating]); } if (pos >= 0) blocked |= ((p_app->settings.ratings_tv >> pos) & (0 | (0x1))); } break; case 1: /* dialogue */ { if (val == 0) break; pos = screen_ratings_tv_xds2pos(p_app->tv_rating); if (pos == ePOS_TV_14) blocked |= ((p_app->settings.ratings_tv >> ePOS_TV_14_D) & (0 | (0x1))); else if (pos == ePOS_TV_PG) blocked |= ((p_app->settings.ratings_tv >> ePOS_TV_PG_D) & (0 | (0x1))); if (rating_str) { strcpy(rating_str, "D"); } } break; case 2: /* language */ { if (val == 0) break; pos = screen_ratings_tv_xds2pos(p_app->tv_rating); if (pos == ePOS_TV_MA) blocked |= ((p_app->settings.ratings_tv >> ePOS_TV_MA_L) & (0 | (0x1))); else if (pos == ePOS_TV_14) blocked |= ((p_app->settings.ratings_tv >> ePOS_TV_14_L) & (0 | (0x1))); else if (pos == ePOS_TV_PG) blocked |= ((p_app->settings.ratings_tv >> ePOS_TV_PG_L) & (0 | (0x1))); if (rating_str) { strcpy(rating_str, "L"); } } break; case 3: /* sex */ { if (val == 0) break; pos = screen_ratings_tv_xds2pos(p_app->tv_rating); if (pos == ePOS_TV_MA) blocked |= ((p_app->settings.ratings_tv >> ePOS_TV_MA_S) & (0 | (0x1))); else if (pos == ePOS_TV_14) blocked |= ((p_app->settings.ratings_tv >> ePOS_TV_14_S) & (0 | (0x1))); else if (pos == ePOS_TV_PG) blocked |= ((p_app->settings.ratings_tv >> ePOS_TV_PG_S) & (0 | (0x1))); if (rating_str) { strcpy(rating_str, "S"); } } break; case 4: /* violence */ { if (val == 0) break; pos = screen_ratings_tv_xds2pos(p_app->tv_rating); if (pos == ePOS_TV_MA) blocked |= ((p_app->settings.ratings_tv >> ePOS_TV_MA_V) & (0 | (0x1))); else if (pos == ePOS_TV_14) blocked |= ((p_app->settings.ratings_tv >> ePOS_TV_14_V) & (0 | (0x1))); else if (pos == ePOS_TV_PG) blocked |= ((p_app->settings.ratings_tv >> ePOS_TV_PG_V) & (0 | (0x1))); if (rating_str) { strcpy(rating_str, "V"); } } break; case 5: /* children */ { if (val == 0) break; pos = 0; /* TV-Y7 and TV-Y map to corresponding XDS TV ratings */ p_app->tv_rating = val; pos = screen_ratings_tv_xds2pos(p_app->tv_rating); if (rating_str) { if (p_app->tv_rating < BAPP_TV_RATING_STR_NUM) strcpy(rating_str, bapp_tv_rating_str[p_app->tv_rating]); } if (pos >= 0) blocked |= ((p_app->settings.ratings_tv >> pos) & (0 | (0x1))); } break; case 6: /* fantasy violence */ { if (val == 0) break; pos = screen_ratings_tv_xds2pos(p_app->tv_rating); if (pos == ePOS_TV_Y7) blocked |= ((p_app->settings.ratings_tv >> ePOS_TV_Y7_FV) & (0 | (0x1))); if (rating_str) { strcpy(rating_str, "FV"); } } break; default: break; } } } if (p_app->settings.ratings_lock == 0) blocked = 0; return blocked; } /* Summary: Process the EIT event descriptors and return > 0 if a change was made to the info structure, 0 if no changes are required and < 0 if an error occured. */ static int chm_process_eit_desc(chm_mgr_t *p_chm, /* Channel manager reference */ unsigned char *msg_buf, /* section buffer containing the EIT table */ int eit_idx, /* EIT event index */ chm_eit_t *p_eit ) { TS_PSI_descriptor eit_desc; int eit_desc_idx,result; bapp_t *p_app = (bapp_t*)p_chm->p_app; char * rating_str; result = 0; /* Process EIT entry descriptors */ eit_desc_idx = 0; memset(p_eit->cc_service,0,MAX_CC_SERVICES * sizeof(PSIP_CSD_service)); while ((eit_desc = PSIP_EIT_getEventDescriptor(p_chm->eit_buf,eit_idx,eit_desc_idx++)) != NULL) { switch (eit_desc[0]) { case 0x87: /* Content advisory descriptor */ { int rr_idx,rd_idx,psip_str_len; PSIP_CAD_rating_region rr; PSIP_CAD_rating_dimension rd; static chm_event_t chm_evt; PSIP_RRT_header rrt_header; int blocked = 0; if (p_app->settings.rrt_status == eRRT_AVAILABLE) { PSIP_RRT_getHeader(p_app->settings.rrt, &rrt_header); } else { rrt_header.rating_region = 1; } p_app->psip_rating_str[0] = 0; for (rr_idx = 0; rr_idx < PSIP_CAD_getRatingRegionCount(eit_desc); ++rr_idx) { if (PSIP_CAD_getRatingRegion(eit_desc,rr_idx,&rr) != BERR_SUCCESS) continue; if ((rr.rating_region != 1) && (rr.rating_region != rrt_header.rating_region)) { BDBG_WRN(("CAD skip %d of %d, rating_region = %d, current = %d\n",rr_idx,PSIP_CAD_getRatingRegionCount(eit_desc),rr.rating_region,rrt_header.rating_region)); continue; } if (rr_idx > 0) { if (strlen(p_app->psip_rating_str) + 4 < MAX_RATING_STR) { psip_str_len = strlen(p_app->psip_rating_str); p_app->psip_rating_str[psip_str_len] = ','; p_app->psip_rating_str[psip_str_len + 1] = ' '; p_app->psip_rating_str[psip_str_len + 2] = 0; } } psip_str_len = strlen(p_app->psip_rating_str); if (chm_process_mss(p_chm,rr.p_rating_description_text,&(p_app->psip_rating_str[psip_str_len]),MAX_RATING_STR - psip_str_len) == 0) { BDBG_WRN(("CAD[%d,%d] %s %d\n",rr_idx,rr.rating_region,p_app->psip_rating_str,rr.rated_dimensions)); rating_str = NULL; } else { BDBG_MSG(("CAD[%d] chm_process_mss failed\n",rr_idx)); rating_str = &(p_app->psip_rating_str[psip_str_len]); } for (rd_idx = 0; rd_idx < rr.rated_dimensions; ++rd_idx) { if (PSIP_CAD_getRatingDimension(eit_desc,rr_idx,rd_idx,&rd) != BERR_SUCCESS) { BDBG_WRN(("PSIP_CAD_getRatingDimension(%d,%d,%d) failed\n",rr.rating_region,rr_idx,rd_idx)); continue; } if (rating_str != NULL) { psip_str_len = strlen(p_app->psip_rating_str); rating_str = &(p_app->psip_rating_str[psip_str_len]); } BDBG_WRN(("*** CAD_RD[%d,%d] region %d, %d-%d\n",rr_idx, rd_idx, rr.rating_region, rd.rating_dimension_j,rd.rating_value)); blocked |= chm_psip_block(p_app, rr.rating_region, rd.rating_dimension_j, rd.rating_value,rating_str); BDBG_WRN(("--- dim:%d value:%d blocked:%d, %s\n", rd.rating_dimension_j, rd.rating_value, blocked,p_app->psip_rating_str)); } if (chm_check_cancel(p_chm)) return eCHM_ERR_CANCELED; } chm_evt.type = eCHM_EVT_CAD; chm_evt.id = (((unsigned int)blocked) << 24) | (((unsigned int)rr.rating_region) << 16) | (((unsigned int)rd.rating_dimension_j) << 8) | ((unsigned int)rd.rating_value); chm_evt.ticks = bos_getticks(); result++; bos_post_event(p_app->msg_queue,(bapp_task_event_t*)&chm_evt); bos_sleep(30); /* yield so other thread can update */ } break; case 0x86: /* Caption Service descriptor */ { int csd_idx; PSIP_CSD_service tmpCSD; for (csd_idx = 0; csd_idx < PSIP_CSD_getNumServices(eit_desc); ++csd_idx) { if (PSIP_CSD_getService(eit_desc,csd_idx,&tmpCSD) != BERR_SUCCESS) { BDBG_WRN(("Error getting CSD %d\n", csd_idx)); continue; } BDBG_MSG(("CSD-%d) %d, %d, %c%c%c\n", csd_idx,tmpCSD.cc_type, tmpCSD.cc.caption_service_number,tmpCSD.language[0],tmpCSD.language[1],tmpCSD.language[2])); if (!tmpCSD.cc_type || (tmpCSD.cc.caption_service_number == 0) || (tmpCSD.cc.caption_service_number > MAX_CC_SERVICES)) continue; memcpy(&(p_eit->cc_service[tmpCSD.cc.caption_service_number - 1]),&tmpCSD,sizeof(PSIP_CSD_service)); } } break; case 0x81: /* AC-3 Audio descriptor */ case 0xAA: /* Redistribution control descriptor */ case 0xB6: /* Content identifier descriptor */ case 0xAB: /* Genre descriptor */ default: BDBG_MSG((" EIT_DESC[0x%02x]\n",eit_desc[0])); break; } } return result; } /* Summary: Get the EIT section and return > 0 if a change was made to the info structure, 0 if no changes are required and < 0 if an error occured. */ static int chm_get_eit(chm_mgr_t *p_chm, /* Channel manager reference */ chm_info_t *p_info, /* info structure to populate */ unsigned short source_id, /* channel source ID from VCT channel */ unsigned short pid /* type type pid */ ) { int eit_events,eit_idx,update,result; unsigned int eit_section_size,utc_time; PSIP_EIT_event eit; bapp_t *p_app = (bapp_t*)p_chm->p_app; unsigned char *p_source_id = (unsigned char*)&source_id; if ((result = chm_get_utctime(p_chm,&utc_time)) != 0) return result; BDBG_MSG(("UTC TIME(%u)\n",utc_time)); update = eEIT_NONE; bmessage_stream_params_init(&p_app->msg_params, p_app->msg); p_app->msg_params.band = p_app->band; p_app->msg_params.pid = pid; /* base pid */ /* Source ID for channel (assumes little endian system!!! )*/ p_app->msg_params.filter.coef[3] = p_source_id[1]; p_app->msg_params.filter.mask[3] = 0x00; p_app->msg_params.filter.excl[3] = 0xff; p_app->msg_params.filter.coef[4] = p_source_id[0]; p_app->msg_params.filter.mask[4] = 0x00; p_app->msg_params.filter.excl[4] = 0xff; /* All PSIP tables have protocol_version which must be = 0 */ p_app->msg_params.filter.coef[8] = 0x00; /* protocol_version */ p_app->msg_params.filter.mask[8] = 0x00; p_app->msg_params.filter.excl[8] = 0xff; eit_section_size = EIT_BUF_LEN; if ((result = chm_getmessage(p_chm,p_chm->eit_buf,&eit_section_size, EIT_TIMEOUT)) != 0) return result; else { if (chm_check_cancel(p_chm)) return eCHM_ERR_CANCELED; if ((result = chm_get_utctime(p_chm,&utc_time)) != 0) return result; utc_time += p_chm->stt.GPS_UTC_offset; eit_events = PSIP_EIT_getNumEvents(p_chm->eit_buf); for (eit_idx = 0; eit_idx < eit_events; eit_idx++) { if (PSIP_EIT_getEvent(p_chm->eit_buf,eit_idx,&eit) != BERR_SUCCESS) continue; #ifdef BCM_DEBUG p_chm->eit_start_time = eit.start_time; p_chm->eit_length = eit.length_in_seconds; #endif if ((utc_time >= eit.start_time) && ( utc_time < (eit.start_time + eit.length_in_seconds))) { chm_process_mss(p_chm,eit.p_title_text, p_info->eit_info[EIT_CURRENT].prog_title,MAX_TITLE_CHARS); p_info->eit_info[EIT_CURRENT].event_id = eit.event_id; p_info->eit_info[EIT_CURRENT].length = eit.length_in_seconds; p_info->eit_info[EIT_CURRENT].start_time = eit.start_time; BDBG_ERR(("EIT Add-%d = %s(%u,%u)\n",EIT_CURRENT,p_info->eit_info[EIT_CURRENT].prog_title,eit.start_time,eit.length_in_seconds)); update |= eEIT_CURRENT; chm_process_eit_desc(p_chm,p_chm->eit_buf,eit_idx,&(p_info->eit_info[EIT_CURRENT])); } else if (((p_info->eit_info[EIT_CURRENT].start_time + p_info->eit_info[EIT_CURRENT].length) >= eit.start_time) && ((p_info->eit_info[EIT_CURRENT].start_time + p_info->eit_info[EIT_CURRENT].length) < (eit.start_time + eit.length_in_seconds))) { chm_process_mss(p_chm,eit.p_title_text, p_info->eit_info[EIT_NEXT].prog_title,MAX_TITLE_CHARS); p_info->eit_info[EIT_NEXT].event_id = eit.event_id; p_info->eit_info[EIT_NEXT].length = eit.length_in_seconds; p_info->eit_info[EIT_NEXT].start_time = eit.start_time; BDBG_MSG(("EIT Add-%d = %s(%d,%u,%u)\n",EIT_NEXT,p_info->eit_info[EIT_NEXT].prog_title, p_info->eit_info[EIT_NEXT].event_id,eit.start_time,eit.length_in_seconds)); update |= eEIT_NEXT; } else { #ifdef BCM_DEBUG chm_process_mss(p_chm,eit.p_title_text, p_chm->tmp_text,MAX_TITLE_CHARS); BDBG_MSG(("EIT Ignore-%d = %s(%d,%u,%u, utc_time=%u)\n",eit_idx,p_chm->tmp_text, p_info->eit_info[EIT_NEXT].event_id,eit.start_time,eit.length_in_seconds,utc_time)); #endif } /* exit if both are found */ if (update == eEIT_BOTH) break; } } return update; } /* Summary: Get the ETT section and return > 0 if a change was made to the info structure, 0 if no changes are required and < 0 if an error occured. */ static int chm_get_ett(chm_mgr_t *p_chm, /* Channel manager reference */ chm_info_t *p_info, /* info structure to populate */ unsigned short source_id, /* channel source ID from VCT channel */ unsigned short pid /* type type pid */ ) { int update; unsigned int ett_section_size; bapp_t *p_app = (bapp_t*)p_chm->p_app; unsigned char *p_source_id = (unsigned char*)&source_id; PSIP_ETT_header ett; int result; update = eEIT_NONE; bmessage_stream_params_init(&p_app->msg_params, p_app->msg); p_app->msg_params.band = p_app->band; p_app->msg_params.pid = pid; /* base pid */ /* Source ID for channel (assumes little endian system!!! )*/ p_app->msg_params.filter.coef[9] = p_source_id[1]; p_app->msg_params.filter.mask[9] = 0x00; p_app->msg_params.filter.excl[9] = 0xff; p_app->msg_params.filter.coef[10] = p_source_id[0]; p_app->msg_params.filter.mask[10] = 0x00; p_app->msg_params.filter.excl[10] = 0xff; /* All PSIP tables have protocol_version which must be = 0 */ p_app->msg_params.filter.coef[8] = 0x00; /* protocol_version */ p_app->msg_params.filter.mask[8] = 0x00; p_app->msg_params.filter.excl[8] = 0xff; ett_section_size = ETT_BUF_LEN; if ((result = chm_getmessage(p_chm,p_chm->eit_buf,&ett_section_size, ETT_TIMEOUT)) != 0) return result; else { PSIP_ETT_getHeader(p_chm->eit_buf,&ett); if (ett.ETM_id.event_id == p_info->eit_info[EIT_CURRENT].event_id) { chm_process_mss(p_chm,ett.p_extended_text_message, p_info->eit_info[EIT_CURRENT].prog_desc,MAX_DESC_CHARS); update |= eEIT_CURRENT; BDBG_MSG(("ETT: 0x%04x(%d) %s\n",pid, EIT_CURRENT,p_info->eit_info[EIT_CURRENT].prog_desc)); } else if (ett.ETM_id.event_id == p_info->eit_info[EIT_NEXT].event_id) { chm_process_mss(p_chm,ett.p_extended_text_message, p_info->eit_info[EIT_NEXT].prog_desc,MAX_DESC_CHARS); update |= eEIT_NEXT; BDBG_MSG(("ETT: 0x%04x(%d) %s\n",pid, EIT_NEXT,p_info->eit_info[EIT_NEXT].prog_desc)); } else { #ifdef BCM_DEBUG chm_process_mss(p_chm,ett.p_extended_text_message, p_chm->tmp_text,MAX_TITLE_CHARS); BDBG_MSG(("ETT: 0x%04x(%d != %d or %d) %s\n",pid,ett.ETM_id.event_id, p_info->eit_info[EIT_CURRENT].event_id,p_info->eit_info[EIT_NEXT].event_id ,p_chm->tmp_text)); BDBG_MSG(("RAW: 0x%02x%02x%02x%02x\n",p_chm->eit_buf[9],p_chm->eit_buf[10],p_chm->eit_buf[11],p_chm->eit_buf[12])); #endif } } return update; } /*RLQ*/ #ifdef DEBUG_RRT static void chm_debug_mgt(unsigned char *msg_buf) { int tables_defined, i; PSIP_MGT_table table; tables_defined = PSIP_MGT_getTablesDefined(msg_buf); for (i = 0; i < tables_defined; i++) { if (PSIP_MGT_getTable( msg_buf, i, &table ) == b_ok) { if ((table.table_type >= 0x0300) && (table.table_type <= 0x03FF)) BDBG_WRN(("RRT: idx%d: Table type=0x%x, pid=0x%x, ver=%d, num_bytes=%d", i, table.table_type, table.table_type_PID, table.table_type_version_number, table.number_bytes)); else if ((table.table_type >= 0x0100) && (table.table_type <= 0x017F)) BDBG_WRN(("EIT: 0x%04x(0x%04x)\n",table.table_type_PID,table.table_type)); } } } #endif /* Summary: process the MGT and associated tables. Returns non-zero on failure. */ static int chm_process_mgt(chm_mgr_t *p_chm, /* Channel manager reference */ unsigned char *msg_buf, /* Buffer containing the MGT table */ unsigned int msg_size, /* PSI buffer size */ chm_info_t *p_info, /* info structure to populate */ bool ext_text, /* Get extended text */ bool redraw /* Send redraw event */ ) { PSIP_MGT_table table; int num_tables, update,result; unsigned short source_id; unsigned int utc_time; bapp_t *p_app = (bapp_t*)p_chm->p_app; source_id = p_info->source_id; num_tables = PSIP_MGT_getTablesDefined(msg_buf); update = eEIT_NONE; result = 0; if ((result = chm_get_utctime(p_chm,&utc_time)) != 0) return result; gettimeofday(&p_chm->mgt_tv); #ifdef DEBUG_RRT /*RLQ*/ chm_debug_mgt(msg_buf); /* DEBUG ONLY */ #endif switch (p_chm->mgt_state) { case eMGT_EIT: for (p_chm->mgt_idx = 0; p_chm->mgt_idx < num_tables; p_chm->mgt_idx++) { if (chm_check_cancel(p_chm)) return eCHM_ERR_CANCELED; PSIP_MGT_getTable(msg_buf,p_chm->mgt_idx,&table); if ((table.table_type >= 0x0100) && (table.table_type <= 0x017F)) { BDBG_MSG(("EIT: 0x%04x(0x%04x)\n",table.table_type_PID,table.table_type)); result = chm_get_eit(p_chm,p_info,source_id,table.table_type_PID); if (result < 0) return result; update |= result; result = 0; if (update != 0) { if (redraw) { chm_make_current(p_chm,p_info); p_chm->chm_evt.type = eCHM_EVT_REDRAW; p_chm->chm_evt.id = p_app->cur_ch_num; chm_post_app_event(p_chm,&p_chm->chm_evt); } if (update == eEIT_BOTH) { p_chm->mgt_state = eMGT_ETT; break; } } } } if (update != eEIT_CURRENT) { result = -1; } else { p_chm->mgt_state = eMGT_ETT; } break; case eMGT_ETT: for (p_chm->mgt_idx = 0; p_chm->mgt_idx < num_tables; p_chm->mgt_idx++) { if (chm_check_cancel(p_chm)) return eCHM_ERR_CANCELED; PSIP_MGT_getTable(msg_buf,p_chm->mgt_idx,&table); if ((table.table_type >= 0x0200) && (table.table_type <= 0x027F)) { BDBG_MSG(("ETT: 0x%04x(0x%04x)\n",table.table_type_PID,table.table_type)); result = chm_get_ett(p_chm,p_info,source_id,table.table_type_PID); if (result < 0) { BDBG_ERR(("chm_get_ett failed, ret=%d\n", result)); p_chm->state = eCHM_PROG_IDLE; return result; } update |= result; result = 0; if (update == eEIT_CURRENT) { if (redraw) { chm_make_current(p_chm,p_info); p_chm->chm_evt.type = eCHM_EVT_REDRAW; p_chm->chm_evt.id = p_app->cur_ch_num; chm_post_app_event(p_chm,&p_chm->chm_evt); } p_chm->state = eCHM_PROG_IDLE; BDBG_MSG(("MGT: DONE\n")); break; } } } p_chm->state = eCHM_PROG_IDLE; break; } if (p_chm->mgt_state == eMGT_ETT) { if (!ext_text) { p_chm->state = eCHM_PROG_IDLE; BDBG_MSG(("MGT: DONE\n")); } } return result; } /****************************************************************************** * INPUTS: context * OUTPUTS: none. * RETURNS: none * FUNCTION: message_callback * DESCRIPTION: Callback function normally called when a complete message has * been received and passed the filter criteria. ******************************************************************************/ #define CHM_STT_BUF_SIZE 0x200 static uint8_t chm_stt_buffer[CHM_STT_BUF_SIZE]; static void *chm_smessage_stt_callback(void * context, size_t size) { int flags; chm_mgr_t * p_chm; struct tm utc_time; struct timeval tv; p_chm = (chm_mgr_t*)context; flags = bos_enter_critical(); PSIP_STT_getHeader(chm_stt_buffer, &p_chm->stt); /* current platform seconds when STT was received */ gettimeofday(&tv); p_chm->sys_offset = tv.tv_sec; p_chm->sys_UTC_offset = p_chm->stt.GPS_UTC_offset; /* Current UTC time according to STT */ p_chm->sys_time = p_chm->stt.system_time - p_chm->stt.GPS_UTC_offset; #if 0 BDBG_ERR(("GPS-%u,GPS_UTC-%u)\n",p_chm->stt.system_time,p_chm->stt.GPS_UTC_offset)); #endif utctime(p_chm->sys_time,&utc_time); p_chm->got_stt = true; bos_exit_critical(flags); #if 0 BDBG_WRN(("STT UTC = %2d/%2d/%4d %2d:%2d:%2d-%s(%2d %2d)\n", utc_time.tm_mon + 1,utc_time.tm_mday,utc_time.tm_year + 1980, utc_time.tm_hour,utc_time.tm_min,utc_time.tm_sec, (p_chm->stt.daylight_savings.DS_status == 0) ? "STD" : "DST", p_chm->stt.daylight_savings.DS_day_of_month,p_chm->stt.daylight_savings.DS_hour )); BDBG_ERR(("STT UTC(%u,%u)\n",p_chm->sys_time,p_chm->sys_offset)); #endif return (void*)chm_stt_buffer; } /* Summary: Get the STT. return non-zero on failure */ static int chm_get_stt(chm_mgr_t *p_chm /* Channel manager reference */ ) { int cerr; smessage_stream_params_t params; bapp_t *p_app = (bapp_t*)p_chm->p_app; cerr = eCHM_ERR_OK; if (NULL != p_chm->stt_msg) { if(b_ok != smessage_stop(p_chm->stt_msg)){ BDBG_ERR(("%s:%d",__FILE__, __LINE__)); cerr = eCHM_ERR_FAILED; goto ExitFunc; } smessage_close(p_chm->stt_msg); p_chm->stt_msg = NULL; } p_chm->stt_msg = smessage_open(bmessage_format_psi); if(NULL == p_chm->stt_msg){ BDBG_ERR(("%s:%d",__FILE__, __LINE__)); cerr = eCHM_ERR_FAILED; goto ExitFunc; } smessage_stream_params_init(¶ms, p_chm->stt_msg); params.band = p_app->band; params.pid = (uint16_t)0x1FFB; params.filter.coef[0] = 0xCD; params.filter.mask[0] = 0x00; /* All PSIP tables have protocol_version which must be = 0 */ params.filter.coef[8] = 0x00; /* protocol_version */ params.filter.mask[8] = 0x00; params.buffer = chm_stt_buffer; params.buffer_size = CHM_STT_BUF_SIZE; params.data_ready_callback = chm_smessage_stt_callback; params.overflow = chm_smessage_overflow; params.callback_context = (void *)p_chm; if(b_ok != smessage_start(¶ms, p_chm->stt_msg)){ BDBG_ERR(("%s:%d",__FILE__, __LINE__)); cerr = eCHM_ERR_FAILED; } p_chm->state = eCHM_GET_VCT; ExitFunc: return cerr; } /* Summary: Get the STT. return non-zero on failure */ static int chm_get_stt2(chm_mgr_t *p_chm /* Channel manager reference */ ) { struct tm utc_time; struct timeval tv; bapp_t *p_app = (bapp_t*)p_chm->p_app; unsigned int section_size,result; bmessage_stream_params_init(&p_app->msg_params, p_app->msg); p_app->msg_params.band = p_app->band; p_app->msg_params.pid = (unsigned short)0x1FFB; /* base pid */ section_size = STT_BUF_LEN; p_app->msg_params.filter.coef[0] = 0xCD; p_app->msg_params.filter.mask[0] = 0x00; p_app->msg_params.filter.excl[0] = 0xff; /* All PSIP tables have protocol_version which must be = 0 */ p_app->msg_params.filter.coef[8] = 0x00; /* protocol_version */ p_app->msg_params.filter.mask[8] = 0x00; p_app->msg_params.filter.excl[8] = 0xff; result = 0; if ((result = chm_getmessage(p_chm,p_chm->stt_buf,§ion_size, STT_TIMEOUT)) != 0) { BDBG_WRN(("Error waiting for STT %d\n",result)); return result; } PSIP_STT_getHeader( p_chm->stt_buf, &p_chm->stt); /* current platform seconds when STT was received */ gettimeofday(&tv); p_chm->sys_offset = tv.tv_sec; p_chm->sys_UTC_offset = p_chm->stt.GPS_UTC_offset; /* Current UTC time according to STT */ p_chm->sys_time = p_chm->stt.system_time - p_chm->stt.GPS_UTC_offset; utctime(p_chm->sys_time,&utc_time); BDBG_WRN(("STT UTC = %2d/%2d/%4d %2d:%2d-%s(%2d %2d)\n", utc_time.tm_mon + 1,utc_time.tm_mday,utc_time.tm_year + 1980, utc_time.tm_hour,utc_time.tm_min, (p_chm->stt.daylight_savings.DS_status == 0) ? "STD" : "DST", p_chm->stt.daylight_savings.DS_day_of_month,p_chm->stt.daylight_savings.DS_hour )); p_chm->got_stt = true; return eCHM_ERR_OK; } /* Summary: Get the VCT. return non-zero on failure */ static int chm_get_vct(chm_mgr_t *p_chm, /* Channel manager reference */ chm_info_t *p_info /* info structure to populate */ ) { bapp_t *p_app= (bapp_t*)p_chm->p_app; unsigned int section_size,section_number; int result = eCHM_ERR_OK; if (p_info->got_vct) { p_chm->state = eCHM_GET_MGT; return result; } section_number = 0; do { /* Get the VCT */ bmessage_stream_params_init(&p_app->msg_params, p_app->msg); p_app->msg_params.band = p_app->band; p_app->msg_params.pid = 0x1ffb; /* VCT */ section_size = VCT_BUF_LEN; p_app->msg_params.filter.coef[0] = 0xc8; /* Table ID for terrestrial */ p_app->msg_params.filter.mask[0] = 0x01; p_app->msg_params.filter.excl[0] = 0xff; p_app->msg_params.filter.coef[6] = section_number; /* section number */ p_app->msg_params.filter.mask[6] = 0x00; p_app->msg_params.filter.excl[6] = 0xff; /* All PSIP tables have protocol_version which must be = 0 */ p_app->msg_params.filter.coef[8] = 0x00; /* protocol_version */ p_app->msg_params.filter.mask[8] = 0x00; p_app->msg_params.filter.excl[8] = 0xff; if ((result = chm_getmessage(p_chm,p_chm->vct_buf,§ion_size, VCT_TIMEOUT)) != 0) { BDBG_WRN(("%s Timeout waiting for VCT (section # %d)\n",__FUNCTION__,section_number)); return result; } if (chm_check_cancel(p_chm)) return eCHM_ERR_CANCELED; if (chm_process_vct(p_chm,p_chm->vct_buf, section_size, p_info,p_app->settings.ch[p_chm->cur_ch_num].minor) != 0) { BDBG_WRN(("VCT entry not found %d\n",section_number)); section_number++; continue; } break; }while (section_size != 0); p_chm->state = eCHM_GET_MGT; return result; } /* Summary: Get the MGT. return non-zero on failure */ static int chm_get_mgt(chm_mgr_t *p_chm /* Channel manager reference */ ) { bapp_t *p_app = (bapp_t*)p_chm->p_app; int result; bmessage_stream_params_init(&p_app->msg_params, p_app->msg); p_app->msg_params.band = p_app->band; p_app->msg_params.pid = (unsigned short)0x1FFB; /* base pid */ p_chm->mgt_size = MGT_BUF_LEN; p_app->msg_params.filter.coef[0] = 0xC7; p_app->msg_params.filter.mask[0] = 0x00; p_app->msg_params.filter.excl[0] = 0xff; /* All PSIP tables have protocol_version which must be = 0 */ p_app->msg_params.filter.coef[8] = 0x00; /* protocol_version */ p_app->msg_params.filter.mask[8] = 0x00; p_app->msg_params.filter.excl[8] = 0xff; if ((result = chm_getmessage(p_chm,p_chm->mgt_buf,&p_chm->mgt_size, MGT_TIMEOUT)) != 0) { BDBG_WRN(("Timeout waiting for MGT\n")); return result; } p_chm->mgt_state = eMGT_EIT; p_chm->state = eCHM_PROCESS_MGT; return 0; } /* Summary: Get the current UTC time, local offset and dst flag. Returns non-zero when it is not possible to determine local time. Currently this function uses XDS local offset over one obtained from the TSIDs */ unsigned int g_xds_time_offset = 0; bool g_xds_dst = false; int chm_get_time_info(chm_mgr_t *p_chm, unsigned int *utc_secs, /* Current UTC time in seconds */ int *local_offset, /* Local time offset from UTC time in seconds */ bool *dst /* Daylight savings flag */ ) { int result; if ((result = chm_get_utctime(p_chm,utc_secs)) != 0) return result; /* Use XDS local time over time obtained from a TSID */ if (g_xds_time_offset != 0) { p_chm->local_offset = 3600 * g_xds_time_offset; p_chm->local_time_source = eTS_XDS; p_chm->local_dst_obs = g_xds_dst; } if (p_chm->local_time_source == eTS_NONE) return eCHM_ERR_FAILED; *local_offset = p_chm->local_offset; *dst = ((p_chm->stt.daylight_savings.DS_status != 0) && p_chm->local_dst_obs) ? true : false; BDBG_MSG(("STT system_time = %u\n",p_chm->stt.system_time)); BDBG_MSG(("STT GPS_UTC_offset = %u\n",p_chm->stt.GPS_UTC_offset)); BDBG_MSG(("STT DS_status = %d\n",p_chm->stt.daylight_savings.DS_status)); BDBG_ERR(("Timesource %d, local_offset = %d, dst\n",p_chm->local_time_source,p_chm->local_offset,p_chm->local_dst_obs)); if (*dst) { *utc_secs += 3600; } return 0; } #ifdef VALIDATE_EIT /* Summary: Determine if the current channel info needs to be updated. */ static bool chm_needs_update(chm_mgr_t *p_chm) { chm_info_t *p_info; unsigned int utc_time,local_offset; bool dst; if (chm_get_time_info(p_chm,&utc_time,&local_offset,&dst) != 0) { BDBG_WRN(("### Invalid UTC time %d\n",utc_time)); return true; } p_info = &p_chm->p_info[p_chm->cur_ch_num]; if ((utc_time >= p_info->eit_info[EIT_CURRENT].start_time) && ( utc_time < (p_info->eit_info[EIT_CURRENT].start_time + p_info->eit_info[EIT_CURRENT].length))) { return false; } BDBG_WRN(("### Entry %d invalid (%d, %d, %d)\n",p_chm->cur_ch_num, utc_time,p_info->eit_info[EIT_CURRENT].start_time, p_info->eit_info[EIT_CURRENT].length)); return true; } #endif /* Summary: Get program guide info. Description: Get program guide info. Returns non-zero on failure. */ static int chm_get_guide(chm_mgr_t *p_chm /* Channel manager reference */ ) { unsigned int utc_secs,cnt,local_offset,idx; bool dst; bapp_t *p_app = (bapp_t*)p_chm->p_app; chm_info_t *p_info; static chm_event_t chm_evt; struct timeval tv; chm_stop_decode(p_chm); /* Check to see if scan was canceled */ if (chm_check_cancel(p_chm)) return eCHM_ERR_FAILED; if ((chm_get_time_info(p_chm,&utc_secs,&local_offset,&dst) != 0) || (p_chm->local_time_source == eTS_NONE)) { BDBG_WRN(("Need local time and UTC offset before guide can be displayed with time info.\n")); } /* Completely reset and start over */ BDBG_MSG(("Get Guide (%d channels).\n",p_app->settings.num_channels)); cnt = 0; p_chm->cur_ch_num = p_app->cur_ch_num; /* Start on current channel */ gettimeofday(&tv); if (tv.tv_sec > (p_chm->guide_tv.tv_sec + (60 * 15))) { for (idx = 0; idx < p_app->settings.num_channels; ++idx) { p_info = &p_chm->p_info[idx]; memset(p_info,0,sizeof(chm_info_t)); p_chm->chm_evt.type = eCHM_EVT_REDRAW; p_chm->chm_evt.id = idx; chm_post_app_event(p_chm,&p_chm->chm_evt); bos_sleep(1); } } gettimeofday(&p_chm->guide_tv); while (p_chm->cmd == eCHM_GUIDE) { if (cnt >= p_app->settings.num_channels) { cnt = 0; p_chm->cur_ch_num = p_app->cur_ch_num; /* Start on current channel */ } if (p_chm->cur_ch_num >= p_app->settings.num_channels) p_chm->cur_ch_num = 0; BDBG_MSG(("Get program info for CH %d of %d\n",p_chm->cur_ch_num,p_app->settings.num_channels)); /* Check to see if scan was canceled */ if (chm_check_cancel(p_chm)) { BDBG_WRN(("Get Guide canceled.\n",p_app->settings.num_channels)); break; } p_info = &p_chm->p_info[p_chm->cur_ch_num]; BDBG_MSG(("### Tune %d\n",p_chm->cur_ch_num)); if (chm_tune_current_freq(p_chm) != 0) { BDBG_WRN(("Tune to channel[%d] %d.%d failed\n",p_chm->cur_ch_num, p_app->settings.ch[p_chm->cur_ch_num].major,p_app->settings.ch[p_chm->cur_ch_num].minor)); p_chm->cur_ch_num++; cnt++; continue; } /* Some channels are sending STT correctly for DST and others do not. To work around this get STT for each channel */ BDBG_MSG(("### Get STT %d\n",p_chm->cur_ch_num)); if (chm_get_stt2(p_chm) != 0) { BDBG_WRN(("Failed to get STT for guide.\n")); p_chm->cur_ch_num++; cnt++; continue; } BDBG_MSG(("### Get VCT %d\n",p_chm->cur_ch_num)); if (chm_get_vct(p_chm,p_info) != 0) { BDBG_WRN(("Failed to get VCT for guide.\n")); p_chm->cur_ch_num++; cnt++; continue; } BDBG_MSG(("### Get MGT %d\n",p_chm->cur_ch_num)); if (chm_get_mgt(p_chm) != 0) { BDBG_WRN(("Failed to get MGT for guide.\n")); p_chm->cur_ch_num++; cnt++; continue; } p_chm->mgt_state = eMGT_EIT; do { BDBG_MSG(("### Process MGT - %d\n",p_chm->mgt_idx)); if (chm_process_mgt(p_chm, p_chm->mgt_buf,p_chm->mgt_size,p_info,false,false) != 0) { BDBG_WRN(("Failed to process MGT for guide.\n")); break; } }while (p_chm->state == eCHM_PROCESS_MGT); BDBG_MSG(("### Send redraw event - %d\n",p_chm->state)); /* JPF Can probably get rid of this when progress is implemented. */ p_chm->chm_evt.type = eCHM_EVT_REDRAW; p_chm->chm_evt.id = p_chm->cur_ch_num; chm_post_app_event(p_chm,&p_chm->chm_evt); p_chm->cur_ch_num++; cnt++; chm_evt.type = eCHM_PROGRESS; chm_evt.id = (100 * cnt) / p_app->settings.num_channels; chm_post_app_event(p_chm,&chm_evt); } BDBG_MSG(("Done processing guide current ch = %d.\n",p_chm->cur_ch_num)); p_chm->chm_evt.type = eCHM_PROGRESS; p_chm->chm_evt.id = 100; chm_post_app_event(p_chm,&p_chm->chm_evt); /* leave guide state */ p_chm->cmd = eCHM_CANCEL; return 0; } /* Summary: Initiate scan for new data */ static int chm_prog_idle(chm_mgr_t *p_chm /* Channel manager reference */ ) { struct timeval tv; gettimeofday(&tv); static unsigned int last_signal_check = 0; if (last_signal_check < bos_getticks()) { chm_send_status(p_chm,0,true); last_signal_check = bos_getticks() + MS_TO_TICKS(CHM_CHECK_SIGNAL_FREQ); } if (tv.tv_sec > (p_chm->mgt_tv.tv_sec + MGT_CYCLE_TIMEOUT)) { bapp_t *p_app = (bapp_t*)p_chm->p_app; if (p_app->chm.local_time_source == eTS_NONE) { p_chm->state = eCHM_GET_VCT; p_chm->p_info[p_chm->cur_ch_num].got_vct = false; } else p_chm->state = eCHM_GET_MGT; } else { } return 0; } /* Summary: Get channel detailed program info. Description: Get channel detailed program info. Returns non-zero on failure. */ static int chm_get_prog_details(chm_mgr_t *p_chm /* Channel manager reference */ ) { chm_state_t orig_state; int result = 0; bapp_t *p_app = (bapp_t*)p_chm->p_app; //unsigned int timeout = bos_getticks() + PROG_INFO_TIMEOUT; orig_state = p_chm->state; if (chm_check_cancel(p_chm)) return result; switch (p_chm->state) { #ifdef BQAM_SCRIPT case eCHM_GET_STT: result = chm_get_stt(p_chm); if (p_app->settings.ratings_lock == 2) chm_get_rrt(p_chm); break; case eCHM_GET_VCT: result = chm_get_vct(p_chm,&p_chm->p_info[p_chm->cur_ch_num]); break; case eCHM_GET_MGT: result = chm_get_mgt(p_chm); break; case eCHM_PROCESS_MGT: result = chm_process_mgt(p_chm, p_chm->mgt_buf,p_chm->mgt_size,&p_chm->p_info[p_chm->cur_ch_num],true,true); break; #else case eCHM_GET_STT: case eCHM_GET_VCT: case eCHM_GET_MGT: case eCHM_PROCESS_MGT: #endif case eCHM_PROG_IDLE: result = chm_prog_idle(p_chm); break; default: break; } if (orig_state != p_chm->state) { BDBG_WRN(("CHM State changed from %s to %s\n",s_chm_state[orig_state],s_chm_state[p_chm->state])); } return result; } /* Summary: Tune to the current frequency. */ static int chm_tune_current_freq(chm_mgr_t *p_chm) { unsigned int freq; bapp_t *p_app = (bapp_t*)p_chm->p_app; if (p_app->settings.ch[p_chm->cur_ch_num].freq_idx == 0x7F) { p_app->band = bstreamer_attach(0,bstream_mpeg_type_ts); } else { freq = s_ch_to_freq[p_app->settings.ch[p_chm->cur_ch_num].freq_idx] * 1000000; if (p_app->settings.ch[p_chm->cur_ch_num].cmd != 0xFF) { bant_send(p_app->settings.ch[p_chm->cur_ch_num].cmd,p_app->settings.ch[p_chm->cur_ch_num].freq_idx + 2); bos_sleep(25); } BDBG_WRN(("Tune to frequency = %d\n",freq)); btuner_ds_params_init(&p_app->ds, p_app->tuner); p_app->ds.cancel_callback = chm_tune_cancel_callback; p_app->ds.cancel_callback_context = p_app; p_app->ds.wait_for_lock = true; #ifdef BQAM_SCRIPT p_app->ds.fecMode = eFEC_ANNEX_B; p_app->ds.qamMode = eQAM_Scan; #endif p_app->band = btuner_tune_ds(p_app->tuner, freq, &p_app->ds); if (p_app->band < 0) { BDBG_WRN(("Tune failed (freq = %d)\n",freq)); chm_send_status(p_chm,freq,false); return eCHM_ERR_FAILED; } } chm_send_status(p_chm,freq,true); chm_stream_close(p_chm); p_app->stream = bstream_open(p_app->band, &p_app->mpeg); if (!p_app->stream) { BDBG_WRN(("bstream_open failed\n")); return eCHM_ERR_FAILED; } return 0; } /* Summary: Start audio and video decode. Description: Handle tuning to the current channel. Returns non-zero on failure. */ #ifdef CONFIG_TEST_STATIC_KEY unsigned char VidEvenControlWord[] = { 0xbe, 0xf9, 0xb0, 0x67,0x13, 0xb8, 0xbc, 0x87}; unsigned char VidOddControlWord[] = { 0xbc, 0xfb, 0xb2, 0x69,0x13, 0xba, 0xbe, 0x8b}; unsigned char AudEvenControlWord[] = { 0xd3, 0x94, 0xdd, 0x44,0x13, 0xd5, 0xd1, 0xb9}; unsigned char AudOddControlWord[] = { 0xd6, 0x91, 0xd8, 0x3f,0x13, 0xd0, 0xd4, 0xb7}; #endif static int chm_start_decode(chm_mgr_t *p_chm) { bapp_t *p_app = (bapp_t*)p_chm->p_app; if (chm_tune_current_freq(p_chm) != 0) { BDBG_WRN(("bapp_tune_current_freq failed\n")); return eCHM_ERR_FAILED; } #ifdef CONFIG_HAS_VIDEO if (bdecode_start(p_app->decode, p_app->stream, p_app->window)) { BDBG_WRN(("bdecode_start failed\n")); return eCHM_ERR_FAILED; } #endif #ifdef CONFIG_HAS_AUDIO if (baudio_decode_start(p_app->audio, (void *)1, p_app->stream)) { BDBG_WRN(("baudio_decode_start failed\n")); return eCHM_ERR_FAILED; } #endif #ifdef CONFIG_TEST_STATIC_KEY { bstream_config config; config.mode = 0; config.key_id = BSETTOP_VIDEO_KEYS; config.feed_key = true; memcpy(config.even_key,VidEvenControlWord,8); memcpy(config.odd_key,VidOddControlWord,8); bstream_set_config(&config); config.key_id = BSETTOP_AUDIO_KEYS; config.feed_key = true; memcpy(config.even_key,AudEvenControlWord,8); memcpy(config.odd_key,AudOddControlWord,8); bstream_set_config(&config); } #endif return 0; } /* Summary: Stop audio and video decode. */ static int chm_stop_decode(chm_mgr_t *p_chm) { bapp_t *p_app = (bapp_t*)p_chm->p_app; p_chm->got_stt = false; p_chm->cur_ch_num = 0xFF; #ifdef CONFIG_HAS_VIDEO if (p_app->decode) bdecode_stop(p_app->decode); #endif #ifdef CONFIG_HAS_AUDIO if (p_app->audio) baudio_decode_stop(p_app->audio); #endif return 0; } /* 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. */ static void chm_config_mpeg(bapp_ch_t *p_ch,bstream_mpeg *p_mpeg, bapp_lang_t pref_lang) { p_mpeg->pcr_pid = p_ch->pcr_pid; p_mpeg->video[0].pid = p_ch->video_pid; if (p_ch->audio_pid[pref_lang] != 0) { p_mpeg->audio[0].pid = p_ch->audio_pid[pref_lang]; } else { bapp_lang_t lang; for (lang = eLANG_ENGLISH; lang < eLANG_MAX; ++lang) { if (p_ch->audio_pid[lang] != 0) { /* choose first non-zero PID */ p_mpeg->audio[0].pid = p_ch->audio_pid[lang]; break; } } } if (p_mpeg->audio[0].pid == 0) { BDBG_WRN(("### All audio PIDs are 0x0000 ###\n")); } } /* 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 chm_tune(chm_mgr_t *p_chm) { int result = eCHM_ERR_FAILED; bapp_t *p_app = (bapp_t*)p_chm->p_app; chm_clear_current(p_chm); if (chm_check_cancel(p_chm)) return result; if (!p_app->settings.num_channels) return result; chm_stop_decode(p_chm); BDBG_WRN(("CH Index = %d\n",p_app->cur_ch_num)); p_chm->cur_ch_num = p_app->cur_ch_num; if (p_app->settings.ch[p_chm->cur_ch_num].pcr_pid != 0) { BDBG_WRN(("Start decode on ch %d.%d (0x%04x,0x%04x)...\n", p_app->settings.ch[p_chm->cur_ch_num].major, p_app->settings.ch[p_chm->cur_ch_num].minor, p_app->settings.ch[p_chm->cur_ch_num].video_pid, p_app->settings.ch[p_chm->cur_ch_num].audio_pid[p_app->lang])); /* start decode success so now transition to info acquire state */ chm_config_mpeg(&p_app->settings.ch[p_chm->cur_ch_num], &p_app->mpeg, p_app->lang); if ((result = chm_start_decode(p_chm)) != 0) { BDBG_WRN(("chm_start_decode failed %d\n",result)); } } if (chm_check_cancel(p_chm)) return result; /* If the pids are not set or the start decode fails try to get program info */ if ((p_app->settings.ch[p_chm->cur_ch_num].pcr_pid == 0) || (result != 0)) { BDBG_WRN(("Reaquire program info failed 0x%04x, %d\n",p_app->settings.ch[p_chm->cur_ch_num].pcr_pid, result)); if (chm_tune_current_freq(p_chm) != 0) { BDBG_WRN(("bapp_tune_current_freq failed\n")); return result; } if (chm_check_cancel(p_chm)) return result; if ((result = chm_get_prog(p_chm,&p_app->settings.ch[p_chm->cur_ch_num], &p_chm->p_info[p_chm->cur_ch_num])) != 0) { BDBG_WRN(("chanmgr_get_prog failed\n")); return result; } if (chm_check_cancel(p_chm)) return result; chm_config_mpeg(&p_app->settings.ch[p_chm->cur_ch_num], &p_app->mpeg, p_app->lang); if ((result = chm_start_decode(p_chm)) != 0) { BDBG_WRN(("chm_start_decode failed\n")); return result; } } if (chm_check_cancel(p_chm)) return result; if (result == 0) { chm_make_current(p_chm,&p_chm->p_info[p_chm->cur_ch_num]); /* start decode success so transition to info acquire state */ p_chm->cmd = eCHM_INFO; } return result; } /* Summary: Do a tune for the purpose of checking the signal strength. */ int chm_check_signal(chm_mgr_t *p_chm) { int result = eCHM_ERR_FAILED; btuner_ds_status ds_status; bapp_t *p_app = (bapp_t*)p_chm->p_app; if (chm_check_cancel(p_chm)) return 0; p_chm->cur_ch_num = p_app->cur_ch_num; if (chm_tune_current_freq(p_chm) != 0) { p_chm->chm_evt.type = eCHM_PROGRESS; p_chm->chm_evt.id = 0; chm_post_app_event(p_chm,&p_chm->chm_evt); BDBG_WRN(("bapp_tune_current_freq failed\n")); return result; } while (!chm_check_cancel(p_chm)) { if (btuner_get_ds_status(p_app->tuner,&ds_status) == b_ok) { p_chm->chm_evt.type = eCHM_PROGRESS; p_chm->chm_evt.id = ds_status.signal_strength; chm_post_app_event(p_chm,&p_chm->chm_evt); } bos_sleep(50); } return result; } /* Summary: Channel Manager Task. */ static void chm_handler(void *data) { unsigned int cmd; chm_cmd_t orig_cmd; chm_mgr_t *p_chm = (chm_mgr_t *)data; while (1) { orig_cmd = p_chm->cmd; switch (p_chm->cmd) { case eCHM_CANCEL: if (p_chm->state > eCHM_GET_STT) p_chm->state = eCHM_GET_STT; cmd = (unsigned int)bos_pend_event(p_chm->queue,-1); if (cmd == 0) break; p_chm->cmd = (chm_cmd_t)(cmd - 1); break; case eCHM_STOP: chm_stop_decode(p_chm); p_chm->cmd = eCHM_CANCEL; break; case eCHM_TUNE: chm_tune(p_chm); break; case eCHM_SCAN: chm_scan(p_chm); break; case eCHM_INFO: chm_get_prog_details(p_chm); break; case eCHM_GUIDE: chm_get_guide(p_chm); break; case eCHM_CHECK_SIGNAL: chm_check_signal(p_chm); break; } if (orig_cmd != p_chm->cmd) { BDBG_WRN(("CHM CMD changed from %s to %s\n", s_chm_cmd_name[orig_cmd],s_chm_cmd_name[p_chm->cmd])); } /* Yield a little */ bos_sleep(10); } } /* Summary: Initialize the channel manager. */ void chm_init(chm_mgr_t *p_chm, void *p_app) { b_task_params params; bresult rc; memset(p_chm,0,sizeof(chm_mgr_t)); p_chm->p_app = p_app; p_chm->cur_ch_num = 0xFF; rc = bos_create_queue(&p_chm->queue,p_chm->event,MAX_CHM_EVENT); BDBG_ASSERT(rc == b_ok); rc = bos_create_queue(&p_chm->gen_queue,p_chm->gen_event,MAX_MSG_EVENTS); BDBG_ASSERT(rc == b_ok); rc = bos_create_mutex(&p_chm->mutex); BDBG_ASSERT(rc == b_ok); p_chm->stack = malloc(CHM_INT_STK_SIZE * 4); BDBG_ASSERT(p_chm->stack); /* round to nearest chm_info_t size */ p_chm->max_info_num = MAX_CHANNELS; p_chm->p_info = (chm_info_t*)malloc(p_chm->max_info_num * sizeof(chm_info_t)); BDBG_ASSERT(p_chm->p_info); memset(p_chm->p_info,0,p_chm->max_info_num * sizeof(chm_info_t)); BDBG_MSG(("Maximum guide entries = %d.\n",p_chm->max_info_num)); params.priority = CHM_INT_PRIORITY; params.stack = p_chm->stack; params.name = "chan_mgr"; params.stack_size = CHM_INT_STK_SIZE; p_chm->vct_buf = malloc(VCT_BUF_LEN); p_chm->pat_buf = malloc(PAT_BUF_LEN); p_chm->pmt_buf = malloc(PMT_BUF_LEN); p_chm->stt_buf = malloc(STT_BUF_LEN); p_chm->eit_buf = malloc(EIT_BUF_LEN); p_chm->ett_buf = malloc(ETT_BUF_LEN); p_chm->mgt_buf = malloc(MGT_BUF_LEN); p_chm->new_rrt_buf = malloc(RRT_BUF_LEN); BDBG_ASSERT(p_chm->vct_buf && p_chm->pat_buf && p_chm->pmt_buf && p_chm->stt_buf && p_chm->eit_buf && p_chm->mgt_buf && p_chm->new_rrt_buf); bos_start_task(&p_chm->task,¶ms,chm_handler,p_chm); BDBG_SetModuleLevel("chan_mgr",BDBG_eMsg); SW_TRACE_SET(SW_TRACE_CHM_INIT); } /* Summary: Function to pass a command to the channel manager. Any results and notification is handled by passing events to the app via the msg_queue. */ void chm_cmd(chm_mgr_t *p_chm, chm_cmd_t cmd) { if ((cmd != eCHM_CANCEL) && (p_chm->cmd != eCHM_CANCEL)) { #if 0 if ((cmd == eCHM_TUNE) && (p_chm->cur_ch_num == p_app->cur_ch_num)) { BDBG_WRN(("Abort %s for %s because current channel (%d) is alread set.\n", s_chm_cmd_name[p_chm->cmd],s_chm_cmd_name[cmd],p_chm->cur_ch_num)); return; } #endif BDBG_WRN(("Canceling pervious command %s for %s\n", s_chm_cmd_name[p_chm->cmd],s_chm_cmd_name[cmd])); bos_post_event(p_chm->queue,(bapp_task_event_t*)(eCHM_CANCEL + 1)); bos_sleep(40); /* yield so thread can process event right away */ } chm_clear_current(p_chm); bos_post_event(p_chm->queue,(bapp_task_event_t*)(cmd + 1)); bos_sleep(10); /* yield so thread can process event right away */ } /* Summary: Get a copy of the current channel manager info structure. */ void chm_get_cur_ch_info(chm_mgr_t *p_chm, chm_info_t *p_info, bstream_mpeg *p_mpeg) { memset(p_info,0,sizeof(chm_info_t)); memset(p_mpeg,0,sizeof(bstream_mpeg)); if (bos_acquire_mutex(&p_chm->mutex,CHM_MUTEX_TIMEOUT) == b_ok) { *p_mpeg = p_chm->mpeg; *p_info = p_chm->cur_info; bos_release_mutex(&p_chm->mutex); } else { BDBG_WRN(("%s:%d mutex timeout\n", __FUNCTION__,__LINE__)); } }