| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /******************************************************************* |
| * FM JNI core |
| * return -1 if error occured. else return needed value. |
| * if return type is char *, return NULL if error occured. |
| * Do NOT return value access paramater. |
| * |
| * FM JNI core should be independent from lower layer, that means |
| * there should be no low layer dependent data struct in FM JNI core |
| * |
| * Naming rule: FMR_n(paramter Micro), FMR_v(functions), fmr_n(global param) |
| * pfmr_n(global paramter pointer) |
| * |
| *******************************************************************/ |
| |
| #include "fmr.h" |
| |
| #ifdef LOG_TAG |
| #undef LOG_TAG |
| #endif |
| #define LOG_TAG "FMLIB_CORE" |
| |
| #define FMR_MAX_IDX 1 |
| |
| struct fmr_ds fmr_data; |
| struct fmr_ds *pfmr_data[FMR_MAX_IDX] = {0}; |
| #define FMR_fd(idx) ((pfmr_data[idx])->fd) |
| #define FMR_err(idx) ((pfmr_data[idx])->err) |
| #define FMR_chip(idx) ((pfmr_data[idx])->cfg_data.chip) |
| #define FMR_low_band(idx) ((pfmr_data[idx])->cfg_data.low_band) |
| #define FMR_high_band(idx) ((pfmr_data[idx])->cfg_data.high_band) |
| #define FMR_seek_space(idx) ((pfmr_data[idx])->cfg_data.seek_space) |
| #define FMR_max_scan_num(idx) ((pfmr_data[idx])->cfg_data.max_scan_num) |
| #define FMR_cbk_tbl(idx) ((pfmr_data[idx])->tbl) |
| #define FMR_cust_hdler(idx) ((pfmr_data[idx])->custom_handler) |
| #define FMR_get_cfg(idx) ((pfmr_data[idx])->get_cfg) |
| |
| int FMR_get_cfgs(int idx) |
| { |
| int ret = -1; |
| FMR_cust_hdler(idx) = NULL; |
| FMR_get_cfg(idx) = NULL; |
| |
| FMR_cust_hdler(idx) = dlopen(CUST_LIB_NAME, RTLD_NOW); |
| if (FMR_cust_hdler(idx) == NULL) { |
| LOGE("%s failed, %s\n", __FUNCTION__, dlerror()); |
| //FMR_seterr(ERR_LD_LIB); |
| } else { |
| *(void **) (&FMR_get_cfg(idx)) = dlsym(FMR_cust_hdler(idx), "CUST_get_cfg"); |
| if (FMR_get_cfg(idx) == NULL) { |
| LOGE("%s failed, %s\n", __FUNCTION__, dlerror()); |
| //FMR_seterr(ERR_FIND_CUST_FNUC); |
| } else { |
| LOGI("Go to run cust function\n"); |
| (*FMR_get_cfg(idx))(&(pfmr_data[idx]->cfg_data)); |
| LOGI("OK\n"); |
| ret = 0; |
| } |
| //dlclose(FMR_cust_hdler(idx)); |
| FMR_cust_hdler(idx) = NULL; |
| FMR_get_cfg(idx) = NULL; |
| } |
| LOGI("%s successfully. chip: 0x%x, lband: %d, hband: %d, seek_space: %d, max_scan_num: %d\n", __FUNCTION__, FMR_chip(idx), FMR_low_band(idx), FMR_high_band(idx), FMR_seek_space(idx), FMR_max_scan_num(idx)); |
| |
| return ret; |
| } |
| |
| int FMR_chk_cfg_data(int idx) |
| { |
| //TODO Need check? how to check? |
| return 0; |
| } |
| |
| static void sig_alarm(int sig) |
| { |
| LOGI("+++Receive sig %d\n", sig); |
| return; |
| } |
| |
| int FMR_init() |
| { |
| int idx; |
| int ret = 0; |
| //signal(4, sig_alarm); |
| |
| for (idx=0; idx<FMR_MAX_IDX; idx++) { |
| if (pfmr_data[idx] == NULL) { |
| break; |
| } |
| } |
| LOGI("FMR idx = %d\n", idx); |
| if (idx == FMR_MAX_IDX) { |
| //FMR_seterr(ERR_NO_MORE_IDX); |
| return -1; |
| } |
| |
| /*The best way here is to call malloc to alloc mem for each idx,but |
| I do not know where to release it, so use static global param instead*/ |
| pfmr_data[idx] = &fmr_data; |
| memset(pfmr_data[idx], 0, sizeof(struct fmr_ds)); |
| |
| if (FMR_get_cfgs(idx) < 0) { |
| LOGI("FMR_get_cfgs failed\n"); |
| goto fail; |
| } |
| |
| if (FMR_chk_cfg_data(idx) < 0) { |
| LOGI("FMR_chk_cfg_data failed\n"); |
| goto fail; |
| } |
| |
| pfmr_data[idx]->init_func = FM_interface_init; |
| if (pfmr_data[idx]->init_func == NULL) { |
| LOGE("%s init_func error, %s\n", __func__, dlerror()); |
| goto fail; |
| } else { |
| LOGI("Go to run init function\n"); |
| (*pfmr_data[idx]->init_func)(&(pfmr_data[idx]->tbl)); |
| LOGI("OK\n"); |
| ret = 0; |
| } |
| |
| return idx; |
| |
| fail: |
| pfmr_data[idx] = NULL; |
| |
| return -1; |
| } |
| |
| int FMR_open_dev(int idx) |
| { |
| int ret = 0; |
| int real_chip; |
| |
| FMR_ASSERT(FMR_cbk_tbl(idx).open_dev); |
| |
| ret = FMR_cbk_tbl(idx).open_dev(FM_DEV_NAME, &FMR_fd(idx)); |
| if (ret || FMR_fd(idx) < 0) { |
| LOGE("%s failed, [fd=%d]\n", __func__, FMR_fd(idx)); |
| return ret; |
| } |
| |
| //Check if customer's cfg matchs driver. |
| ret = FMR_get_chip_id(idx, &real_chip); |
| if (FMR_chip(idx) != real_chip) { |
| LOGE("%s, Chip config error. 0x%x\n", __func__, real_chip); |
| ret = FMR_cbk_tbl(idx).close_dev(FMR_fd(idx)); |
| return ret; |
| } |
| |
| LOGD("%s, [fd=%d] [chipid=0x%x] [ret=%d]\n", __func__, FMR_fd(idx), real_chip, ret); |
| return ret; |
| } |
| |
| int FMR_close_dev(int idx) |
| { |
| int ret = 0; |
| |
| FMR_ASSERT(FMR_cbk_tbl(idx).close_dev); |
| ret = FMR_cbk_tbl(idx).close_dev(FMR_fd(idx)); |
| LOGD("%s, [fd=%d] [ret=%d]\n", __func__, FMR_fd(idx), ret); |
| return ret; |
| } |
| |
| int FMR_pwr_up(int idx, int freq) |
| { |
| int ret = 0; |
| |
| FMR_ASSERT(FMR_cbk_tbl(idx).pwr_up); |
| |
| LOGI("%s,[freq=%d]\n", __func__, freq); |
| if (freq < fmr_data.cfg_data.low_band || freq > fmr_data.cfg_data.high_band) { |
| LOGE("%s error freq: %d\n", __func__, freq); |
| ret = -ERR_INVALID_PARA; |
| return ret; |
| } |
| ret = FMR_cbk_tbl(idx).pwr_up(FMR_fd(idx), fmr_data.cfg_data.band, freq); |
| if (ret) { |
| LOGE("%s failed, [ret=%d]\n", __func__, ret); |
| } |
| fmr_data.cur_freq = freq; |
| LOGD("%s, [ret=%d]\n", __func__, ret); |
| return ret; |
| } |
| |
| int FMR_pwr_down(int idx, int type) |
| { |
| int ret = 0; |
| |
| FMR_ASSERT(FMR_cbk_tbl(idx).pwr_down); |
| ret = FMR_cbk_tbl(idx).pwr_down(FMR_fd(idx), type); |
| LOGD("%s, [ret=%d]\n", __func__, ret); |
| return ret; |
| } |
| |
| int FMR_get_chip_id(int idx, int *chipid) |
| { |
| int ret = 0; |
| |
| FMR_ASSERT(FMR_cbk_tbl(idx).get_chip_id); |
| FMR_ASSERT(chipid); |
| |
| ret = FMR_cbk_tbl(idx).get_chip_id(FMR_fd(idx), chipid); |
| if (ret) { |
| LOGE("%s failed, %s\n", __func__, FMR_strerr()); |
| *chipid = -1; |
| } |
| LOGD("%s, [ret=%d]\n", __func__, ret); |
| return ret; |
| } |
| |
| int FMR_get_ps(int idx, uint8_t **ps, int *ps_len) |
| { |
| int ret = 0; |
| |
| FMR_ASSERT(FMR_cbk_tbl(idx).get_ps); |
| FMR_ASSERT(ps); |
| FMR_ASSERT(ps_len); |
| ret = FMR_cbk_tbl(idx).get_ps(FMR_fd(idx), &fmr_data.rds, ps, ps_len); |
| LOGD("%s, [ret=%d]\n", __func__, ret); |
| return ret; |
| } |
| |
| int FMR_get_rt(int idx, uint8_t **rt, int *rt_len) |
| { |
| int ret = 0; |
| |
| FMR_ASSERT(FMR_cbk_tbl(idx).get_rt); |
| FMR_ASSERT(rt); |
| FMR_ASSERT(rt_len); |
| |
| ret = FMR_cbk_tbl(idx).get_rt(FMR_fd(idx), &fmr_data.rds, rt, rt_len); |
| LOGD("%s, [ret=%d]\n", __func__, ret); |
| return ret; |
| } |
| |
| int FMR_tune(int idx, int freq) |
| { |
| int ret = 0; |
| |
| FMR_ASSERT(FMR_cbk_tbl(idx).tune); |
| |
| ret = FMR_cbk_tbl(idx).tune(FMR_fd(idx), freq, fmr_data.cfg_data.band); |
| if (ret) { |
| LOGE("%s failed, [ret=%d]\n", __func__, ret); |
| } |
| fmr_data.cur_freq = freq; |
| LOGD("%s, [freq=%d] [ret=%d]\n", __func__, freq, ret); |
| return ret; |
| } |
| |
| /*return: fm_true: desense, fm_false: not desene channel*/ |
| fm_bool FMR_DensenseDetect(fm_s32 idx, fm_u16 ChannelNo, fm_s32 RSSI) |
| { |
| fm_u8 bDesenseCh = 0; |
| |
| bDesenseCh = FMR_cbk_tbl(idx).desense_check(FMR_fd(idx), ChannelNo, RSSI); |
| if (bDesenseCh == 1) { |
| return fm_true; |
| } |
| return fm_false; |
| } |
| |
| fm_bool FMR_SevereDensense(fm_u16 ChannelNo, fm_s32 RSSI) |
| { |
| fm_s32 i = 0, j = 0; |
| struct fm_fake_channel_t *chan_info = fmr_data.cfg_data.fake_chan; |
| |
| ChannelNo /= 10; |
| |
| for (i=0; i<chan_info->size; i++) { |
| if (ChannelNo == chan_info->chan[i].freq) { |
| //if (RSSI < FM_SEVERE_RSSI_TH) |
| if (RSSI < chan_info->chan[i].rssi_th) { |
| LOGI(" SevereDensense[%d] RSSI[%d]\n", ChannelNo,RSSI); |
| return fm_true; |
| } else { |
| break; |
| } |
| } |
| } |
| return fm_false; |
| } |
| #if (FMR_NOISE_FLOORT_DETECT==1) |
| /*return TRUE:get noise floor freq*/ |
| fm_bool FMR_NoiseFloorDetect(fm_bool *rF, fm_s32 rssi, fm_s32 *F_rssi) |
| { |
| if (rF[0] == fm_true) { |
| if (rF[1] == fm_true) { |
| F_rssi[2] = rssi; |
| rF[2] = fm_true; |
| return fm_true; |
| } else { |
| F_rssi[1] = rssi; |
| rF[1] = fm_true; |
| } |
| } else { |
| F_rssi[0] = rssi; |
| rF[0] = fm_true; |
| } |
| return fm_false; |
| } |
| #endif |
| |
| /*check the cur_freq->freq is valid or not |
| return fm_true : need check cur_freq->valid |
| fm_false: check faild, should stop seek |
| */ |
| static fm_bool FMR_Seek_TuneCheck(int idx, fm_softmute_tune_t *cur_freq) |
| { |
| int ret = 0; |
| if (fmr_data.scan_stop == fm_true) { |
| ret = FMR_tune(idx,fmr_data.cur_freq); |
| LOGI("seek stop!!! tune ret=%d",ret); |
| return fm_false; |
| } |
| ret = FMR_cbk_tbl(idx).soft_mute_tune(FMR_fd(idx), cur_freq); |
| if (ret) { |
| LOGE("soft mute tune, failed:[%d]\n",ret); |
| cur_freq->valid = fm_false; |
| return fm_true; |
| } |
| if (cur_freq->valid == fm_true)/*get valid channel*/ { |
| if (FMR_DensenseDetect(idx, cur_freq->freq, cur_freq->rssi) == fm_true) { |
| LOGI("desense channel detected:[%d] \n", cur_freq->freq); |
| cur_freq->valid = fm_false; |
| return fm_true; |
| } |
| if (FMR_SevereDensense(cur_freq->freq, cur_freq->rssi) == fm_true) { |
| LOGI("sever desense channel detected:[%d] \n", cur_freq->freq); |
| cur_freq->valid = fm_false; |
| return fm_true; |
| } |
| LOGI("seek result freq:[%d] \n", cur_freq->freq); |
| } |
| return fm_true; |
| } |
| /* |
| check more 2 freq, curfreq: current freq, seek_dir: 1,forward. 0,backword |
| */ |
| static int FMR_Seek_More(int idx, fm_softmute_tune_t *validfreq, fm_u8 seek_dir, fm_u8 step, fm_u16 min_freq, fm_u16 max_freq) |
| { |
| fm_s32 i; |
| fm_softmute_tune_t cur_freq; |
| cur_freq.freq = validfreq->freq; |
| |
| for (i=0; i<2; i++) { |
| if (seek_dir)/*forward*/ { |
| if (cur_freq.freq + step > max_freq) { |
| return 0; |
| } |
| cur_freq.freq += step; |
| } else/*backward*/ { |
| if (cur_freq.freq - step < min_freq) { |
| return 0; |
| } |
| cur_freq.freq -= step; |
| } |
| if (FMR_Seek_TuneCheck(idx, &cur_freq) == fm_true) { |
| if (cur_freq.valid == fm_true) { |
| if (cur_freq.rssi > validfreq->rssi) { |
| validfreq->freq = cur_freq.freq; |
| validfreq->rssi = cur_freq.rssi; |
| LOGI("seek cover last by freq=%d",cur_freq.freq); |
| } |
| } |
| } else { |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| /*check the a valid channel |
| return -1 : seek fail |
| 0: seek success |
| */ |
| int FMR_seek_Channel(int idx, int start_freq, int min_freq, int max_freq, int band_channel_no, int seek_space, int dir, int *ret_freq, int *rssi_tmp) |
| { |
| fm_s32 i, ret = 0; |
| fm_softmute_tune_t cur_freq; |
| |
| if (dir == 1)/*forward*/ { |
| for (i=((start_freq-min_freq)/seek_space+1); i<band_channel_no; i++) { |
| cur_freq.freq = min_freq + seek_space*i; |
| LOGI("i=%d, freq=%d-----1",i,cur_freq.freq); |
| ret = FMR_Seek_TuneCheck(idx, &cur_freq); |
| if (ret == fm_false) { |
| return -1; |
| } else { |
| if (cur_freq.valid == fm_false) { |
| continue; |
| } else { |
| if (FMR_Seek_More(idx, &cur_freq, dir, seek_space, min_freq, max_freq) == 0) { |
| *ret_freq = cur_freq.freq; |
| *rssi_tmp = cur_freq.rssi; |
| return 0; |
| } else { |
| return -1; |
| } |
| } |
| } |
| } |
| for (i=0; i<((start_freq-min_freq)/seek_space); i++) { |
| cur_freq.freq = min_freq + seek_space*i; |
| LOGI("i=%d, freq=%d-----2",i,cur_freq.freq); |
| ret = FMR_Seek_TuneCheck(idx, &cur_freq); |
| if (ret == fm_false) { |
| return -1; |
| } else { |
| if (cur_freq.valid == fm_false) { |
| continue; |
| } else { |
| if (FMR_Seek_More(idx, &cur_freq, dir, seek_space, min_freq, max_freq) == 0) { |
| *ret_freq = cur_freq.freq; |
| *rssi_tmp = cur_freq.rssi; |
| return 0; |
| } else { |
| return -1; |
| } |
| } |
| } |
| } |
| } else/*backward*/ { |
| for (i=((start_freq-min_freq)/seek_space-1); i>=0; i--) { |
| cur_freq.freq = min_freq + seek_space*i; |
| LOGI("i=%d, freq=%d-----3",i,cur_freq.freq); |
| ret = FMR_Seek_TuneCheck(idx, &cur_freq); |
| if (ret == fm_false) { |
| return -1; |
| } else { |
| if (cur_freq.valid == fm_false) { |
| continue; |
| } else { |
| if (FMR_Seek_More(idx, &cur_freq, dir, seek_space, min_freq, max_freq) == 0) { |
| *ret_freq = cur_freq.freq; |
| *rssi_tmp = cur_freq.rssi; |
| return 0; |
| } else { |
| return -1; |
| } |
| } |
| } |
| } |
| for (i=(band_channel_no-1); i>((start_freq-min_freq)/seek_space); i--) { |
| cur_freq.freq = min_freq + seek_space*i; |
| LOGI("i=%d, freq=%d-----4",i,cur_freq.freq); |
| ret = FMR_Seek_TuneCheck(idx, &cur_freq); |
| if (ret == fm_false) { |
| return -1; |
| } else { |
| if (cur_freq.valid == fm_false) { |
| continue; |
| } else { |
| if (FMR_Seek_More(idx, &cur_freq, dir,seek_space, min_freq, max_freq) == 0) { |
| *ret_freq = cur_freq.freq; |
| *rssi_tmp = cur_freq.rssi; |
| return 0; |
| } else { |
| return -1; |
| } |
| } |
| } |
| } |
| } |
| |
| *ret_freq = start_freq; |
| return 0; |
| } |
| |
| int FMR_seek(int idx, int start_freq, int dir, int *ret_freq) |
| { |
| fm_s32 ret = 0, i, j; |
| fm_softmute_tune_t cur_freq; |
| fm_s32 band_channel_no = 0; |
| fm_u8 seek_space = 10; |
| fm_u16 min_freq, max_freq; |
| int rssi; |
| |
| if ((start_freq < 7600) || (start_freq > 10800)) /*need replace by macro*/ { |
| LOGE("%s error start_freq: %d\n", __func__, start_freq); |
| return -ERR_INVALID_PARA; |
| } |
| |
| //FM radio seek space,5:50KHZ; 1:100KHZ; 2:200KHZ |
| if (fmr_data.cfg_data.seek_space == 5) { |
| seek_space = 5; |
| } else if (fmr_data.cfg_data.seek_space == 2) { |
| seek_space = 20; |
| } else { |
| seek_space = 10; |
| } |
| if (fmr_data.cfg_data.band == FM_BAND_JAPAN)/* Japan band 76MHz ~ 90MHz */ { |
| band_channel_no = (9600-7600)/seek_space + 1; |
| min_freq = 7600; |
| max_freq = 9600; |
| } else if (fmr_data.cfg_data.band == FM_BAND_JAPANW)/* Japan wideband 76MHz ~ 108MHz */ { |
| band_channel_no = (10800-7600)/seek_space + 1; |
| min_freq = 7600; |
| max_freq = 10800; |
| } else/* US/Europe band 87.5MHz ~ 108MHz (DEFAULT) */ { |
| band_channel_no = (10800-8750)/seek_space + 1; |
| min_freq = 8750; |
| max_freq = 10800; |
| } |
| |
| fmr_data.scan_stop = fm_false; |
| LOGD("seek start freq %d band_channel_no=[%d], seek_space=%d band[%d - %d] dir=%d\n", start_freq, band_channel_no,seek_space,min_freq,max_freq,dir); |
| |
| ret = FMR_seek_Channel(idx, start_freq, min_freq, max_freq, band_channel_no, seek_space, dir, ret_freq, &rssi); |
| |
| return ret; |
| } |
| |
| int FMR_set_mute(int idx, int mute) |
| { |
| int ret = 0; |
| |
| FMR_ASSERT(FMR_cbk_tbl(idx).set_mute) |
| |
| if ((mute < 0) || (mute > 1)) { |
| LOGE("%s error param mute: %d\n", __func__, mute); |
| } |
| |
| ret = FMR_cbk_tbl(idx).set_mute(FMR_fd(idx), mute); |
| if (ret) { |
| LOGE("%s failed, %s\n", __func__, FMR_strerr()); |
| } |
| LOGD("%s, [mute=%d] [ret=%d]\n", __func__, mute, ret); |
| return ret; |
| } |
| |
| int FMR_is_rdsrx_support(int idx, int *supt) |
| { |
| int ret = 0; |
| |
| FMR_ASSERT(FMR_cbk_tbl(idx).is_rdsrx_support); |
| FMR_ASSERT(supt); |
| |
| ret = FMR_cbk_tbl(idx).is_rdsrx_support(FMR_fd(idx), supt); |
| if (ret) { |
| *supt = 0; |
| LOGE("%s, failed\n", __func__); |
| } |
| LOGD("%s, [supt=%d] [ret=%d]\n", __func__, *supt, ret); |
| return ret; |
| } |
| |
| int FMR_Pre_Search(int idx) |
| { |
| //avoid scan stop flag clear if stop cmd send before pre-search finish |
| fmr_data.scan_stop = fm_false; |
| FMR_ASSERT(FMR_cbk_tbl(idx).pre_search); |
| FMR_cbk_tbl(idx).pre_search(FMR_fd(idx)); |
| return 0; |
| } |
| |
| int FMR_Restore_Search(int idx) |
| { |
| FMR_ASSERT(FMR_cbk_tbl(idx).restore_search); |
| FMR_cbk_tbl(idx).restore_search(FMR_fd(idx)); |
| return 0; |
| } |
| |
| int FMR_scan_Channels(int idx, uint16_t *scan_tbl, int *max_cnt, fm_s32 band_channel_no, fm_u16 Start_Freq, fm_u8 seek_space, fm_u8 NF_Space) |
| { |
| fm_s32 ret = 0, Num = 0, i, j; |
| fm_u32 ChannelNo = 0; |
| fm_softmute_tune_t cur_freq; |
| static struct fm_cqi SortData[CQI_CH_NUM_MAX]; |
| fm_bool LastExist = fm_false; |
| struct fm_cqi swap; |
| #if (FMR_NOISE_FLOORT_DETECT==1) |
| fm_s32 Pacc = 0, Nacc = 0; |
| fm_s32 NF = 0; |
| fm_bool F[3] = {fm_false, fm_false, fm_false}; |
| fm_s32 F_Rssi[3] = {0}; |
| fm_u8 NF_Idx = 0; |
| #endif |
| |
| memset(SortData, 0, CQI_CH_NUM_MAX*sizeof(struct fm_cqi)); |
| LOGI("band_channel_no=[%d], seek_space=%d, start freq=%d\n", band_channel_no,seek_space,Start_Freq); |
| for (i=0; i<band_channel_no; i++) { |
| if (fmr_data.scan_stop == fm_true) { |
| FMR_Restore_Search(idx); |
| ret = FMR_tune(idx, fmr_data.cur_freq); |
| LOGI("scan stop!!! tune ret=%d",ret); |
| return -1; |
| } |
| cur_freq.freq = Start_Freq + seek_space*i; |
| ret = FMR_cbk_tbl(idx).soft_mute_tune(FMR_fd(idx), &cur_freq); |
| if (ret) { |
| LOGE("soft mute tune, failed:[%d]\n",ret); |
| LastExist = fm_false; |
| continue; |
| } |
| if (cur_freq.valid == fm_true)/*get valid channel*/ { |
| #if (FMR_NOISE_FLOORT_DETECT==1) |
| memset(F, fm_false, sizeof(F)); |
| #endif |
| if (FMR_DensenseDetect(idx, cur_freq.freq, cur_freq.rssi) == fm_true) { |
| LOGI("desense channel detected:[%d] \n", cur_freq.freq); |
| LastExist = fm_false; |
| continue; |
| } |
| if ((LastExist == fm_true) && (Num > 0)) /*neighbor channel*/ { |
| if (cur_freq.rssi>SortData[Num-1].rssi)/*save current freq and cover last channel*/ { |
| if (FMR_SevereDensense(cur_freq.freq, cur_freq.rssi) == fm_true) { |
| LastExist = fm_false; |
| continue; |
| } |
| SortData[Num-1].ch=cur_freq.freq; |
| SortData[Num-1].rssi=cur_freq.rssi; |
| SortData[Num-1].reserve = 1; |
| LOGI("cover last channel \n"); |
| } else/*ignore current freq*/ { |
| LastExist = fm_false; |
| continue; |
| } |
| } else/*save current*/ { |
| if (FMR_SevereDensense(cur_freq.freq, cur_freq.rssi) == fm_true) { |
| LastExist = fm_false; |
| continue; |
| } |
| SortData[Num].ch = cur_freq.freq; |
| SortData[Num].rssi = cur_freq.rssi; |
| SortData[Num].reserve = 1; |
| Num++; |
| LastExist = fm_true; |
| LOGI("Num++:[%d] \n", Num); |
| } |
| } else { |
| #if (FMR_NOISE_FLOORT_DETECT==1) |
| if (FMR_DensenseDetect(idx, cur_freq.freq, cur_freq.rssi) == fm_false) { |
| if (FMR_NoiseFloorDetect(F, cur_freq.rssi, F_Rssi) == fm_true) { |
| Pacc += F_Rssi[1]; |
| Nacc++; |
| /*check next freq*/ |
| F[0] = F[1]; |
| F_Rssi[0] = F_Rssi[1]; |
| F[1] = F[2]; |
| F_Rssi[1] = F_Rssi[2]; |
| F[2] = fm_false; |
| F_Rssi[2] = 0; |
| LOGI("FM Noise FLoor:Pacc=[%d] Nacc=[%d] \n", Pacc,Nacc); |
| } |
| } else { |
| memset(F, fm_false, sizeof(F)); |
| } |
| #endif |
| LastExist = fm_false; |
| } |
| #if (FMR_NOISE_FLOORT_DETECT==1) |
| if (((i%NF_Space) == 0) && (i != 0)) { |
| if (Nacc > 0) { |
| NF = Pacc/Nacc; |
| } else { |
| NF = RSSI_TH-FM_NOISE_FLOOR_OFFSET; |
| } |
| Pacc = 0; |
| Nacc = 0; |
| for (j=NF_Idx; j<Num; j++) { |
| if (SortData[j].rssi < (NF+FM_NOISE_FLOOR_OFFSET)) { |
| LOGI("FM Noise FLoor Detected:freq=[%d] NF=[%d] \n", SortData[j].ch,NF); |
| SortData[j].reserve = 0; |
| } |
| } |
| NF_Idx = j; |
| LOGI("FM Noise FLoor NF_Idx[%d] \n", NF_Idx); |
| } |
| #endif |
| } |
| LOGI("get channel no.[%d] \n", Num); |
| if (Num == 0)/*get nothing*/ { |
| *max_cnt = 0; |
| FMR_Restore_Search(idx); |
| return -1; |
| } |
| for (i=0; i<Num; i++)/*debug*/ { |
| LOGI("[%d]:%d \n", i,SortData[i].ch); |
| } |
| |
| switch (fmr_data.cfg_data.scan_sort) |
| { |
| case FM_SCAN_SORT_UP: |
| case FM_SCAN_SORT_DOWN: |
| { |
| LOGI("Start sort \n"); |
| //do sort: insert sort algorithm |
| for (i = 1; i < Num; i++) { |
| for (j = i; (j > 0) && ((FM_SCAN_SORT_DOWN == fmr_data.cfg_data.scan_sort) ? (SortData[j-1].rssi \ |
| < SortData[j].rssi) : (SortData[j-1].rssi > SortData[j].rssi)); j--) { |
| memcpy(&swap, &SortData[j], sizeof(struct fm_cqi)); |
| memcpy(&SortData[j], &SortData[j-1], sizeof(struct fm_cqi)); |
| memcpy(&SortData[j-1], &swap, sizeof(struct fm_cqi)); |
| } |
| } |
| LOGI("End sort \n"); |
| break; |
| } |
| default: |
| break; |
| } |
| |
| ChannelNo = 0; |
| for (i=0; i<Num; i++) { |
| if (SortData[i].reserve == 1) { |
| SortData[i].ch /= 10; |
| |
| scan_tbl[ChannelNo]=SortData[i].ch; |
| ChannelNo++; |
| } |
| } |
| *max_cnt=ChannelNo; |
| |
| LOGI("return channel no.[%d] \n", ChannelNo); |
| return 0; |
| } |
| |
| int FMR_scan(int idx, uint16_t *scan_tbl, int *max_cnt) |
| { |
| fm_s32 ret = 0; |
| fm_s32 band_channel_no = 0; |
| fm_u8 seek_space = 10; |
| fm_u16 Start_Freq = 8750; |
| fm_u8 NF_Space = 41; |
| |
| //FM radio seek space,5:50KHZ; 1:100KHZ; 2:200KHZ |
| if (fmr_data.cfg_data.seek_space == 5) { |
| seek_space = 5; |
| } else if (fmr_data.cfg_data.seek_space == 2) { |
| seek_space = 20; |
| } else { |
| seek_space = 10; |
| } |
| if (fmr_data.cfg_data.band == FM_BAND_JAPAN)/* Japan band 76MHz ~ 90MHz */ { |
| band_channel_no = (9600-7600)/seek_space + 1; |
| Start_Freq = 7600; |
| NF_Space = 400/seek_space; |
| } else if (fmr_data.cfg_data.band == FM_BAND_JAPANW)/* Japan wideband 76MHZ ~ 108MHz */ { |
| band_channel_no = (10800-7600)/seek_space + 1; |
| Start_Freq = 7600; |
| NF_Space = 640/seek_space; |
| } else/* US/Europe band 87.5MHz ~ 108MHz (DEFAULT) */ { |
| band_channel_no = (10800-8750)/seek_space + 1; |
| Start_Freq = 8750; |
| NF_Space = 410/seek_space; |
| } |
| |
| ret = FMR_scan_Channels(idx, scan_tbl, max_cnt, band_channel_no, Start_Freq, seek_space, NF_Space); |
| |
| return ret; |
| } |
| |
| int FMR_stop_scan(int idx) |
| { |
| fmr_data.scan_stop = fm_true; |
| return 0; |
| } |
| |
| int FMR_turn_on_off_rds(int idx, int onoff) |
| { |
| int ret = 0; |
| |
| FMR_ASSERT(FMR_cbk_tbl(idx).turn_on_off_rds) |
| ret = FMR_cbk_tbl(idx).turn_on_off_rds(FMR_fd(idx), onoff); |
| if (ret) { |
| LOGE("%s, failed\n", __func__); |
| } |
| LOGD("%s, [onoff=%d] [ret=%d]\n", __func__, onoff, ret); |
| return ret; |
| } |
| |
| int FMR_read_rds_data(int idx, uint16_t *rds_status) |
| { |
| int ret = 0; |
| |
| FMR_ASSERT(FMR_cbk_tbl(idx).read_rds_data); |
| FMR_ASSERT(rds_status); |
| |
| ret = FMR_cbk_tbl(idx).read_rds_data(FMR_fd(idx), &fmr_data.rds, rds_status); |
| /*if (ret) { |
| LOGE("%s, get no event\n", __func__); |
| }*/ |
| LOGD("%s, [status=%d] [ret=%d]\n", __func__, *rds_status, ret); |
| return ret; |
| } |
| |
| int FMR_active_af(int idx, uint16_t *ret_freq) |
| { |
| int ret = 0; |
| |
| FMR_ASSERT(FMR_cbk_tbl(idx).active_af); |
| FMR_ASSERT(ret_freq); |
| ret = FMR_cbk_tbl(idx).active_af(FMR_fd(idx), |
| &fmr_data.rds, |
| fmr_data.cfg_data.band, |
| fmr_data.cur_freq, |
| ret_freq); |
| if ((ret == 0) && (*ret_freq != fmr_data.cur_freq)) { |
| fmr_data.cur_freq = *ret_freq; |
| LOGI("active AF OK, new channel[freq=%d]\n", fmr_data.cur_freq); |
| } |
| LOGD("%s, [ret=%d]\n", __func__, ret); |
| return ret; |
| } |
| |
| int FMR_ana_switch(int idx, int antenna) |
| { |
| int ret = 0; |
| |
| FMR_ASSERT(FMR_cbk_tbl(idx).ana_switch); |
| |
| if (fmr_data.cfg_data.short_ana_sup == true) { |
| ret = FMR_cbk_tbl(idx).ana_switch(FMR_fd(idx), antenna); |
| if (ret) { |
| LOGE("%s failed, [ret=%d]\n", __func__, ret); |
| } |
| } else { |
| LOGW("FM antenna switch not support!\n"); |
| ret = -ERR_UNSUPT_SHORTANA; |
| } |
| |
| LOGD("%s, [ret=%d]\n", __func__, ret); |
| return ret; |
| } |
| |