/*************************************************************** ** ** Broadcom Corp. Confidential ** Copyright 2003-2008 Broadcom Corp. All Rights Reserved. ** ** 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. ** ** File: si_lvct.c ** Description: function that parses the L-VCT table sections and ** keeps track of all the virtual channel link lists. ** ** Created: 03/08/2001 ** ** REVISION: ** ** $Log: $ ** ** ****************************************************************/ #include "si.h" #include "si_os.h" #include "si_dbg.h" #include "si_util.h" #include "si_list.h" #include "si_vct.h" #include "si_lvct.h" #include "si_descriptors.h" /* local function prototypes. */ static SI_LVCT_CHANNEL * SI_LVCT_Create_Channel (void); static unsigned char SI_LVCT_Compare_channel(SI_LVCT_CHANNEL *chan1, SI_LVCT_CHANNEL *chan2); static SI_RET_CODE SI_LVCT_Free_Channel(SI_LVCT_CHANNEL *channel); static SI_RET_CODE SI_LVCT_Free_List(void); static SI_RET_CODE SI_LVCT_Ins_Channel(SI_LVCT_CHANNEL *new_channel); struct lvct_channel_list LVCT_channels; /*LVCT_2_channels*/ unsigned long Total_LVCT_Channels; unsigned char LVCT_version_number; unsigned long LVCT_section_mask[8]; SI_mutex m_lvct; void SI_LVCT_Init(void) { unsigned long i; SI_LST_D_INIT(&LVCT_channels); Total_LVCT_Channels = 0; LVCT_version_number = 0xff; for (i=0; i<8; i++) LVCT_section_mask[i] = 0; SI_mutex_init(m_lvct); } /********************************************************************* Function : SI_LVCT_Create_Channel Description : Function to allocate the space for an L-VCT channel. Input : none. Output : pointer to the L-VCT channel structure allocated. Will return NULL if out of memory. **********************************************************************/ static SI_LVCT_CHANNEL * SI_LVCT_Create_Channel (void) { SI_LVCT_CHANNEL * lvct_channel; int i; lvct_channel = (SI_LVCT_CHANNEL *)SI_alloc(sizeof(SI_LVCT_CHANNEL)); if (lvct_channel == NULL) { SI_DBG_PRINT(E_SI_ERR_MSG,("Failed to allocate an L-VCT channel!!!\n")); return NULL; } SI_LST_D_INIT_ENTRY(&(lvct_channel->chan_link)); lvct_channel->ext_name_len = 0; lvct_channel->ext_name = NULL; lvct_channel->num_of_ts_serv = 0; lvct_channel->time_shifted = NULL; return lvct_channel; } /********************************************************************* Function : SI_LVCT_Compare_Channel Description : Function to compare an LVCT channel to another to see if the virtual channel numbers are equal, greater or less. Input : SI_LVCT_CHANNEL *chan1. pointer to one LVCT channel struct SI_LVCT_CHANNEL *chan2. pointer to second LVCT channel struct Output : 0 if the chan1 number is smaller, 1 if equal, 2 if chan1 is greater. **********************************************************************/ static unsigned char SI_LVCT_Compare_channel(SI_LVCT_CHANNEL *chan1, SI_LVCT_CHANNEL *chan2) { if (chan1->vcn_mode != chan2->vcn_mode) if (chan1->vcn_mode == ONE_PART) return 0; else return 2; /* both channels are one or two part. */ if (chan1->vcn_mode == ONE_PART) { if (chan1->channum1 == chan2->channum1) return 1; else if (chan1->channum1 > chan2->channum1) return 2; } else { if (chan1->channum1 == chan2->channum1) { if (chan1->channum2 == chan2->channum2) return 1; else if (chan1->channum2 > chan2->channum2) return 2; } else if (chan1->channum1 > chan2->channum1) return 2; } return 0; } /********************************************************************* Function : SI_LVCT_Free_Channel Description : Function to free an LVCT channel from the LVCT channel list. the channel structure will be freed but not removed from the channel list. WE ASSUME THAT WHEN CALLING THIS FUNCTION, THE CHANNEL HAS NOT BEEN ADDED TO THE LIST YET!!! Input : SI_LVCT_CHANNEL *channel. pointer to LVCT channel structure to be freed. Output : SI_RET_CODE. **********************************************************************/ static SI_RET_CODE SI_LVCT_Free_Channel(SI_LVCT_CHANNEL *channel) { if (channel) { if (channel->ext_name) SI_free(channel->ext_name); if (channel->time_shifted) SI_free(channel->time_shifted); SI_free(channel); } return SI_SUCCESS; } /********************************************************************* Function : SI_LVCT_Free_List Description : Function to free the whole LVCT channel list. Input : None. Output : SI_RET_CODE. **********************************************************************/ static SI_RET_CODE SI_LVCT_Free_List(void) { SI_LVCT_CHANNEL *channel; SI_mutex_lock(m_lvct); while ((channel = SI_LST_D_FIRST(&LVCT_channels))) { SI_LST_D_REMOVE_HEAD(&LVCT_channels, chan_link); SI_LVCT_Free_Channel(channel); Total_LVCT_Channels--; } Total_LVCT_Channels = 0; /* just to be sure. */ SI_mutex_unlock(m_lvct); return SI_SUCCESS; } /********************************************************************* Function : SI_LVCT_Ins_Channel Description : Function to insert an LVCT channel into the LVCT channel list. The order is that two part numbers are after the one part numbers and within each part the channels are sorted in incrementing order. Input : SI_LVCT_CHANNEL *new_channel. pointer to new LVCT channel structure to be inserted. Output : SI_RET_CODE. **********************************************************************/ static SI_RET_CODE SI_LVCT_Ins_Channel(SI_LVCT_CHANNEL *new_channel) { SI_LVCT_CHANNEL * channel; unsigned char comp; int i; SI_mutex_lock(m_lvct); channel = SI_LST_D_FIRST(&LVCT_channels); /* if the list is empty, just put the new channel in. */ if (channel == NULL) { SI_LST_D_INSERT_HEAD(&LVCT_channels, new_channel, chan_link); Total_LVCT_Channels++; SI_mutex_unlock(m_lvct); return SI_SUCCESS; } /* search for the the place to insert. */ while ((comp = SI_LVCT_Compare_channel(new_channel, channel)) == 2 && SI_LST_D_NEXT(channel, chan_link)) channel = SI_LST_D_NEXT(channel, chan_link); if (comp == 2) { /* we got to the end of list. insert after current element. */ SI_LST_D_INSERT_AFTER(channel, new_channel, chan_link); Total_LVCT_Channels++; } else if (comp == 0) { /* insert before the current element. */ SI_LST_D_INSERT_BEFORE(&LVCT_channels, channel, new_channel, chan_link); Total_LVCT_Channels++; } else { /* equal! It should not happen. But if it does, simply keep it and free the new one. */ SI_DBG_PRINT(E_SI_WRN_MSG,("LVCT the channel already exists! keeping it and free newbie!\n")); SI_LVCT_Free_Channel(new_channel); } SI_mutex_unlock(m_lvct); return SI_SUCCESS; } /********************************************************************* Function : SI_LVCT_Parse Description : Function to parse a newly received LVCT table section and put it into the LVCT channel link list Input : unsigned char *lvct_table : newly received LVCT table data. Output : SI_RET_CODE. **********************************************************************/ SI_RET_CODE SI_LVCT_Parse (unsigned char *lvct_table) { unsigned long temp, i, j, offset; unsigned long section_length, version_number; unsigned long section_number, last_section_number, num_channels_in_section; unsigned long desc_start; unsigned long desc_tag, desc_len, len; unsigned short major_num, minor_num; unsigned char *current; SI_LVCT_CHANNEL * channel; SI_RET_CODE result; SI_DBG_PRINT(E_SI_DBG_MSG,("LVCT Table received.\n")); temp = *lvct_table; if (temp != SI_LVCT_TABLE_ID) { SI_DBG_PRINT(E_SI_ERR_MSG,("LVCT Table ID error!!! %x\n", temp)); return SI_TABLE_ID_ERROR; } /* calculate and check section length. */ section_length = SI_Construct_Data(lvct_table, LVCT_SECTION_LENGTH_BYTE_INDX, LVCT_SECTION_LENGTH_BYTE_NUM, LVCT_SECTION_LENGTH_SHIFT, LVCT_SECTION_LENGTH_MASK); section_length += LVCT_SECTION_LENGTH_BYTE_INDX+LVCT_SECTION_LENGTH_BYTE_NUM; if (section_length > SI_LONG_SECTION_LENGTH) { SI_DBG_PRINT(E_SI_ERR_MSG,("LVCT Table section length error!!! %x\n", section_length)); return SI_SECTION_LENGTH_ERROR; } /* We do the CRC check here to verify the contents of this section. */ if (SI_CRC32_Check(lvct_table, section_length) != SI_SUCCESS) { SI_DBG_PRINT(E_SI_ERR_MSG,("LVCT Table section CRC error!!!\n")); return SI_CRC_ERROR; } /* look at current_next_indicator. It should be 1 for AEIT. */ temp = SI_Construct_Data(lvct_table, LVCT_CURRENT_NEXT_INDICATOR_BYTE_INDX, LVCT_CURRENT_NEXT_INDICATOR_BYTE_NUM, LVCT_CURRENT_NEXT_INDICATOR_SHIFT, LVCT_CURRENT_NEXT_INDICATOR_MASK); if (temp != 1) { SI_DBG_PRINT(E_SI_DBG_MSG,("LVCT Table current_next_indicator not one. discarding it.%x\n", temp)); return SI_SUCCESS; } /* check to make sure protocol version is zero. */ temp = SI_Construct_Data(lvct_table, LVCT_PROTOCOL_VERSION_BYTE_INDX, LVCT_PROTOCOL_VERSION_BYTE_NUM, LVCT_PROTOCOL_VERSION_SHIFT, LVCT_PROTOCOL_VERSION_MASK); if (temp != SI_CURRENT_PROTOCOL_VERSION) { SI_DBG_PRINT(E_SI_ERR_MSG,("LVCT Table PROTOCOL version error!!! %x\n", temp)); return SI_PROTOCOL_VER_ERROR; } /* now we know where the slot is in the link list, See if we need to update. */ version_number = SI_Construct_Data(lvct_table, LVCT_VERSION_NUMBER_BYTE_INDX, LVCT_VERSION_NUMBER_BYTE_NUM, LVCT_VERSION_NUMBER_SHIFT, LVCT_VERSION_NUMBER_MASK); section_number = SI_Construct_Data(lvct_table, LVCT_SECTION_NUMBER_BYTE_INDX, LVCT_SECTION_NUMBER_BYTE_NUM, LVCT_SECTION_NUMBER_SHIFT, LVCT_SECTION_NUMBER_MASK); if (LVCT_version_number == version_number) { /* the same version number. Now check if the section number has already be processed. */ if (SI_Chk_Section_mask(LVCT_section_mask, section_number)) { /* section already processed, we are done! */ SI_DBG_PRINT(E_SI_DBG_MSG,("LVCT Table section does not need to be updated!\n")); return SI_SUCCESS; } else SI_DBG_PRINT(E_SI_DBG_MSG,("New LVCT Table section received!\n")); } else { /* different version number. free the old channel link list. */ SI_DBG_PRINT(E_SI_DBG_MSG,("New LVCT Table version received!\n")); /* init section mask. */ last_section_number = SI_Construct_Data(lvct_table, LVCT_LAST_SECTION_NUMBER_BYTE_INDX, LVCT_LAST_SECTION_NUMBER_BYTE_NUM, LVCT_LAST_SECTION_NUMBER_SHIFT, LVCT_LAST_SECTION_NUMBER_MASK); SI_Init_Section_Mask(LVCT_section_mask, last_section_number); LVCT_version_number = version_number; /* free the old list. */ SI_LVCT_Free_List(); } /* update section mask here. */ SI_Set_Section_mask(LVCT_section_mask, section_number); num_channels_in_section = SI_Construct_Data(lvct_table, LVCT_NUM_CHANNELS_BYTE_INDX, LVCT_NUM_CHANNELS_BYTE_NUM, LVCT_NUM_CHANNELS_SHIFT, LVCT_NUM_CHANNELS_MASK); /* get channels one by one. */ current = lvct_table+LVCT_NUM_CHANNELS_BYTE_INDX+LVCT_NUM_CHANNELS_BYTE_NUM; for (i=0; ishort_name[j] = ((((unsigned short)(*(current++)))<<8)&0xff00); channel->short_name[j] |= (((unsigned short)(*(current++)))&0x00ff); } /* get channel number(s). */ major_num = SI_Construct_Data(current, LVCT_MAJOR_NUMBER_BYTE_INDX, LVCT_MAJOR_NUMBER_BYTE_NUM, LVCT_MAJOR_NUMBER_SHIFT, LVCT_MAJOR_NUMBER_MASK); minor_num = SI_Construct_Data(current, LVCT_MINOR_NUMBER_BYTE_INDX, LVCT_MINOR_NUMBER_BYTE_NUM, LVCT_MINOR_NUMBER_SHIFT, LVCT_MINOR_NUMBER_MASK); if ((major_num&0x03f0) == 0x03f0) { channel->vcn_mode = ONE_PART; channel->channum1 = ((major_num&0x0f)<<10) + minor_num; SI_DBG_PRINT(E_SI_DBG_MSG,("Channel %x: ", channel->channum1)); } else { channel->vcn_mode = TWO_PART; channel->channum1 = major_num; channel->channum2 = minor_num; SI_DBG_PRINT(E_SI_DBG_MSG,("Channel %x-%x: ", channel->channum1, channel->channum2)); } for (j=0; jshort_name[j]&0xff))); /* get other parameters. */ channel->mod_mode = SI_Construct_Data(current, LVCT_MODULATION_MODE_BYTE_INDX, LVCT_MODULATION_MODE_BYTE_NUM, LVCT_MODULATION_MODE_SHIFT, LVCT_MODULATION_MODE_MASK); channel->carrier_freq = SI_Construct_Data(current, LVCT_CARRIER_FREQUENCY_BYTE_INDX, LVCT_CARRIER_FREQUENCY_BYTE_NUM, LVCT_CARRIER_FREQUENCY_SHIFT, LVCT_CARRIER_FREQUENCY_MASK); channel->tsid = SI_Construct_Data(current, LVCT_CHANNEL_TSID_BYTE_INDX, LVCT_CHANNEL_TSID_BYTE_NUM, LVCT_CHANNEL_TSID_SHIFT, LVCT_CHANNEL_TSID_MASK); channel->program_num = SI_Construct_Data(current, LVCT_PROGRAM_NUMBER_BYTE_INDX, LVCT_PROGRAM_NUMBER_BYTE_NUM, LVCT_PROGRAM_NUMBER_SHIFT, LVCT_PROGRAM_NUMBER_MASK); channel->chanbits = SI_Construct_Data(current, LVCT_CHANNEL_BITS_BYTE_INDX, LVCT_CHANNEL_BITS_BYTE_NUM, LVCT_CHANNEL_BITS_SHIFT, LVCT_CHANNEL_BITS_MASK); channel->serv_type = SI_Construct_Data(current, LVCT_SERVICE_TYPE_BYTE_INDX, LVCT_SERVICE_TYPE_BYTE_NUM, LVCT_SERVICE_TYPE_SHIFT, LVCT_SERVICE_TYPE_MASK); channel->source_ID = SI_Construct_Data(current, LVCT_SOURCE_ID_BYTE_INDX, LVCT_SOURCE_ID_BYTE_NUM, LVCT_SOURCE_ID_SHIFT, LVCT_SOURCE_ID_MASK); SI_DBG_PRINT(E_SI_DBG_MSG,("mod %x, carrire %x hz, tsid %x, prog %x, serv type %x, src id %x ", channel->mod_mode,channel->carrier_freq,channel->tsid,channel->program_num,channel->serv_type,channel->source_ID)); SI_DBG_PRINT(E_SI_DBG_MSG,("\n")); /* get descriptor length. */ desc_len = SI_Construct_Data(current, LVCT_DESC_LENGTH_BYTE_INDX, LVCT_DESC_LENGTH_BYTE_NUM, LVCT_DESC_LENGTH_SHIFT, LVCT_DESC_LENGTH_MASK); /* go through all descriptors. */ current += LVCT_DESC_LENGTH_BYTE_INDX+LVCT_DESC_LENGTH_BYTE_NUM; offset = 0; while (offset < desc_len) { desc_tag = *(current++); /* points to desc len. */ len = *(current++); /* point to first desc data. */ switch(desc_tag) { case SI_DESC_EXTENDED_CHANNEL_NAME: SI_DBG_PRINT(E_SI_DBG_MSG,("LVCT Table: ext channel name descriptor received.\n")); channel->ext_name_len = len; if ((channel->ext_name = SI_alloc(channel->ext_name_len)) == NULL) { SI_DBG_PRINT(E_SI_ERR_MSG,("LVCT Table failed to alloc mem for channel ext name!!!\n")); SI_LVCT_Free_Channel(channel); return SI_NO_MEMORY; } SI_memcpy(channel->ext_name, current, len); current += len; /* point to next desc. */ offset += len; break; case SI_DESC_TIME_SHIFTED_SERVICE: SI_DBG_PRINT(E_SI_DBG_MSG,("LVCT Table: Time shifted descriptor received.\n")); channel->num_of_ts_serv = SI_Construct_Data(current, DESC_TSS_NUM_OF_SERV_BYTE_INDEX, DESC_TSS_NUM_OF_SERV_BYTE_NUM, DESC_TSS_NUM_OF_SERV_SHIFT, DESC_TSS_NUM_OF_SERV_MASK); current += DESC_TSS_NUM_OF_SERV_BYTE_INDEX+DESC_TSS_NUM_OF_SERV_BYTE_NUM; offset += DESC_TSS_NUM_OF_SERV_BYTE_INDEX+DESC_TSS_NUM_OF_SERV_BYTE_NUM; if ( (channel->time_shifted = SI_alloc(channel->num_of_ts_serv*sizeof(TIME_SHIFT_SERV))) == NULL) { SI_DBG_PRINT(E_SI_ERR_MSG,("LVCT Table failed to alloc mem for time shift serv!!!\n")); SI_LVCT_Free_Channel(channel); return SI_NO_MEMORY; } for (j=0; jnum_of_ts_serv; j++) { channel->time_shifted[j].time_shift = SI_Construct_Data(current, DESC_TSS_TIME_SHIFT_BYTE_INDEX, DESC_TSS_TIME_SHIFT_BYTE_NUM, DESC_TSS_TIME_SHIFT_SHIFT, DESC_TSS_TIME_SHIFT_MASK); major_num = SI_Construct_Data(current, DESC_TSS_MAJOR_NUM_BYTE_INDEX, DESC_TSS_MAJOR_NUM_BYTE_NUM, DESC_TSS_MAJOR_NUM_SHIFT, DESC_TSS_MAJOR_NUM_MASK); minor_num = SI_Construct_Data(current, DESC_TSS_MINOR_NUM_BYTE_INDEX, DESC_TSS_MINOR_NUM_BYTE_NUM, DESC_TSS_MINOR_NUM_SHIFT, DESC_TSS_MINOR_NUM_MASK); if ((major_num&0x03f0) == 0x03f0) { channel->time_shifted[j].vcn_mode = ONE_PART; channel->time_shifted[j].channum1 = ((major_num&0x0f)<<10) + minor_num; } else { channel->time_shifted[j].vcn_mode = TWO_PART; channel->time_shifted[j].channum1 = major_num; channel->time_shifted[j].channum2 = minor_num; } current += DESC_TSS_MINOR_NUM_BYTE_INDEX+DESC_TSS_MINOR_NUM_BYTE_NUM; offset += DESC_TSS_MINOR_NUM_BYTE_INDEX+DESC_TSS_MINOR_NUM_BYTE_NUM; } break; default: if(desc_tag != SI_DESC_STUFFING) SI_DBG_PRINT(E_SI_WRN_MSG,("LVCT channel descriptor %x received!\n", desc_tag)); current += len; /* point to next desc. */ offset += len; break; } } /* make sure descriptor len works out. */ if (offset != desc_len) { SI_DBG_PRINT(E_SI_ERR_MSG,("LVCT Table channel descriptor error!!!\n")); SI_LVCT_Free_Channel(channel); return SI_DESCRIPTOR_ERROR; } /* insert the channel in the list. */ SI_LVCT_Ins_Channel(channel); } /* TBD just skip the table descriptors and check for length. */ desc_len = SI_Construct_Data(current, LVCT_ADD_DESC_LENGTH_BYTE_INDX, LVCT_ADD_DESC_LENGTH_BYTE_NUM, LVCT_ADD_DESC_LENGTH_SHIFT, LVCT_ADD_DESC_LENGTH_MASK); current += LVCT_ADD_DESC_LENGTH_BYTE_INDX+LVCT_ADD_DESC_LENGTH_BYTE_NUM; offset = 0; while (offset < desc_len) { if ((desc_tag = *(current++)) != SI_DESC_STUFFING) SI_DBG_PRINT(E_SI_WRN_MSG,("LVCT table descriptor %x received! Ignoring!\n", desc_tag)); len = *(current++); current += len; offset += len+2; } if (offset != desc_len) { SI_DBG_PRINT(E_SI_ERR_MSG,("LVCT Table section descriptor length error!!!\n")); return SI_DESCRIPTOR_ERROR; } if ((unsigned long)(current-lvct_table) != section_length-SI_CRC_LENGTH) { SI_DBG_PRINT(E_SI_ERR_MSG,("LVCT Table length error!!!\n")); return SI_SECTION_LENGTH_ERROR; } return SI_SUCCESS; }