diff options
author | Benson Huang <benson.huang@mediatek.com> | 2014-12-18 18:05:37 +0800 |
---|---|---|
committer | Leo Wang <leozwang@google.com> | 2014-12-22 19:56:42 +0000 |
commit | 0f37d60a1cc38c9233a2298f2851ac2b408eabca (patch) | |
tree | 1115d0180a14f063821229d5d9adbe26c9f6185d | |
parent | 4b523a3c2a376bef6440eb9504a0d33875bad842 (diff) | |
download | android_packages_apps_FMRadio-0f37d60a1cc38c9233a2298f2851ac2b408eabca.tar.gz android_packages_apps_FMRadio-0f37d60a1cc38c9233a2298f2851ac2b408eabca.tar.bz2 android_packages_apps_FMRadio-0f37d60a1cc38c9233a2298f2851ac2b408eabca.zip |
[FM] Move FM Radio JNI
The CL is to add jni folder in packages/apps/FMRadio.
https://partner-android-review.googlesource.com/#/c/189642/3
Bug 18632972
Change-Id: I1740bdbabae13109a821abfdb25b848baf6ca84b
Signed-off-by: Benson Huang <benson.huang@mediatek.com>
-rw-r--r-- | jni/Android.mk | 17 | ||||
-rw-r--r-- | jni/fmr/Android.mk | 34 | ||||
-rw-r--r-- | jni/fmr/common.cpp | 590 | ||||
-rw-r--r-- | jni/fmr/fmr.h | 230 | ||||
-rw-r--r-- | jni/fmr/fmr_core.cpp | 843 | ||||
-rw-r--r-- | jni/fmr/fmr_err.cpp | 39 | ||||
-rw-r--r-- | jni/fmr/libfm_jni.cpp | 437 |
7 files changed, 2190 insertions, 0 deletions
diff --git a/jni/Android.mk b/jni/Android.mk new file mode 100644 index 0000000..493157c --- /dev/null +++ b/jni/Android.mk @@ -0,0 +1,17 @@ +# 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. + +LOCAL_PATH:= $(call my-dir) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/jni/fmr/Android.mk b/jni/fmr/Android.mk new file mode 100644 index 0000000..d8c2a95 --- /dev/null +++ b/jni/fmr/Android.mk @@ -0,0 +1,34 @@ +# 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. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + fmr_core.cpp \ + fmr_err.cpp \ + libfm_jni.cpp \ + common.cpp + +LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) \ + frameworks/base/include/media + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libdl \ + libmedia \ + +LOCAL_MODULE := libfmjni +include $(BUILD_SHARED_LIBRARY) diff --git a/jni/fmr/common.cpp b/jni/fmr/common.cpp new file mode 100644 index 0000000..859d3b3 --- /dev/null +++ b/jni/fmr/common.cpp @@ -0,0 +1,590 @@ +/* + * 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. + */ + +#include "fmr.h" + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "FMLIB_COM" + +static int g_stopscan = 0; + +int COM_open_dev(const char *pname, int *fd) +{ + int ret = 0; + int tmp = -1; + + FMR_ASSERT(pname); + FMR_ASSERT(fd); + + LOGI("COM_open_dev start\n"); + tmp = open(pname, O_RDWR); + if (tmp < 0) { + LOGE("Open %s failed, %s\n", pname, strerror(errno)); + ret = -ERR_INVALID_FD; + } + *fd = tmp; + LOGI("%s, [fd=%d] [ret=%d]\n", __func__, *fd, ret); + return ret; +} + +int COM_close_dev(int fd) +{ + int ret = 0; + + LOGI("COM_close_dev start\n"); + ret = close(fd); + if (ret) { + LOGE("%s, failed\n", __func__); + } + LOGD("%s, [fd=%d] [ret=%d]\n", __func__, fd, ret); + return ret; +} + +int COM_pwr_up(int fd, int band, int freq) +{ + int ret = 0; + struct fm_tune_parm parm; + + LOGI("%s, [freq=%d]\n", __func__, freq); + bzero(&parm, sizeof(struct fm_tune_parm)); + + parm.band = band; + parm.freq = freq; + parm.hilo = FM_AUTO_HILO_OFF; + parm.space = FM_SEEK_SPACE; + + ret = ioctl(fd, FM_IOCTL_POWERUP, &parm); + if (ret) { + LOGE("%s, failed\n", __func__); + } + LOGD("%s, [fd=%d] [ret=%d]\n", __func__, fd, ret); + return ret; +} + +int COM_pwr_down(int fd, int type) +{ + int ret = 0; + LOGI("%s, [type=%d]\n", __func__, type); + ret = ioctl(fd, FM_IOCTL_POWERDOWN, &type); + if (ret) { + LOGE("%s, failed\n", __func__); + } + LOGD("%s, [fd=%d] [ret=%d]\n", __func__, fd, ret); + return ret; +} + +/*0x20: space, 0x7E:~*/ +#define ISVALID(c)((c)>=0x20 && (c)<=0x7E) +/*change any char which out of [0x20,0x7E]to space(0x20)*/ +void COM_change_string(uint8_t *str, int len) +{ + int i = 0; + for (i=0; i<len; i++) { + if (false == ISVALID(str[i])) { + str[i]= 0x20; + } + } +} + +int COM_get_ps(int fd, RDSData_Struct *rds, uint8_t **ps, int *ps_len) +{ + int ret = 0; + char tmp_ps[9] = {0}; + + FMR_ASSERT(rds); + FMR_ASSERT(ps); + FMR_ASSERT(ps_len); + + if (rds->event_status&RDS_EVENT_PROGRAMNAME) { + LOGD("%s, Success,[event_status=%d]\n", __func__, rds->event_status); + *ps = &rds->PS_Data.PS[3][0]; + *ps_len = sizeof(rds->PS_Data.PS[3]); + + COM_change_string(*ps, *ps_len); + memcpy(tmp_ps, *ps, 8); + LOGI("PS=%s\n", tmp_ps); + } else { + LOGE("%s, Failed,[event_status=%d]\n", __func__, rds->event_status); + *ps = NULL; + *ps_len = 0; + ret = -ERR_RDS_NO_DATA; + } + + return ret; +} + +int COM_get_rt(int fd, RDSData_Struct *rds, uint8_t **rt, int *rt_len) +{ + int ret = 0; + char tmp_rt[65] = { 0 }; + + FMR_ASSERT(rds); + FMR_ASSERT(rt); + FMR_ASSERT(rt_len); + + if (rds->event_status&RDS_EVENT_LAST_RADIOTEXT) { + LOGD("%s, Success,[event_status=%d]\n", __func__, rds->event_status); + *rt = &rds->RT_Data.TextData[3][0]; + *rt_len = rds->RT_Data.TextLength; + + COM_change_string(*rt, *rt_len); + memcpy(tmp_rt, *rt, 64); + LOGI("RT=%s\n", tmp_rt); + } else { + LOGE("%s, Failed,[event_status=%d]\n", __func__, rds->event_status); + *rt = NULL; + *rt_len = 0; + ret = -ERR_RDS_NO_DATA; + } + return ret; +} + +int COM_get_pi(int fd, RDSData_Struct *rds, uint16_t *pi) +{ + int ret = 0; + + FMR_ASSERT(rds); + FMR_ASSERT(pi); + + if (rds->event_status&RDS_EVENT_PI_CODE) { + LOGD("%s, Success,[event_status=%d] [PI=%d]\n", __func__, rds->event_status, rds->PI); + *pi = rds->PI; + } else { + LOGI("%s, Failed, there's no pi,[event_status=%d]\n", __func__, rds->event_status); + *pi = -1; + ret = -ERR_RDS_NO_DATA; + } + + return ret; +} + +int COM_tune(int fd, int freq, int band) +{ + int ret = 0; + + struct fm_tune_parm parm; + + bzero(&parm, sizeof(struct fm_tune_parm)); + + parm.band = band; + parm.freq = freq; + parm.hilo = FM_AUTO_HILO_OFF; + parm.space = FM_SEEK_SPACE; + + ret = ioctl(fd, FM_IOCTL_TUNE, &parm); + if (ret) { + LOGE("%s, failed\n", __func__); + } + LOGD("%s, [fd=%d] [freq=%d] [ret=%d]\n", __func__, fd, freq, ret); + return ret; +} + +int COM_seek(int fd, int *freq, int band, int dir, int lev) +{ + int ret = 0; + struct fm_seek_parm parm; + + bzero(&parm, sizeof(struct fm_tune_parm)); + + parm.band = band; + parm.freq = *freq; + parm.hilo = FM_AUTO_HILO_OFF; + parm.space = FM_SEEK_SPACE; + if (dir == 1) { + parm.seekdir = FM_SEEK_UP; + } else if (dir == 0) { + parm.seekdir = FM_SEEK_DOWN; + } + parm.seekth = lev; + + ret = ioctl(fd, FM_IOCTL_SEEK, &parm); + if (ret == 0) { + *freq = parm.freq; + } + LOGD("%s, [fd=%d] [ret=%d]\n", __func__, fd, ret); + return ret; +} + +int COM_set_mute(int fd, int mute) +{ + int ret = 0; + int tmp = mute; + + LOGD("%s, start \n", __func__); + ret = ioctl(fd, FM_IOCTL_MUTE, &tmp); + if (ret) { + LOGE("%s, failed\n", __func__); + } + LOGD("%s, [fd=%d] [ret=%d]\n", __func__, fd, ret); + return ret; +} + +int COM_is_fm_pwrup(int fd, int *pwrup) +{ + int ret = 0; + + ret = ioctl(fd, FM_IOCTL_IS_FM_POWERED_UP, pwrup); + if (ret) { + LOGE("%s, failed\n", __func__); + } + LOGD("%s, [fd=%d] [ret=%d]\n", __func__, fd, ret); + return ret; +} + +/****************************************** + * Inquiry if RDS is support in driver. + * Parameter: + * None + *supt Value: + * 1: support + * 0: NOT support + * -1: error + ******************************************/ +int COM_is_rdsrx_support(int fd, int *supt) +{ + int ret = 0; + int support = -1; + + if (fd < 0) { + LOGE("FM isRDSsupport fail, g_fm_fd = %d\n", fd); + *supt = -1; + ret = -ERR_INVALID_FD; + return ret; + } + + ret = ioctl(fd, FM_IOCTL_RDS_SUPPORT, &support); + if (ret) { + LOGE("FM FM_IOCTL_RDS_SUPPORT fail, errno = %d\n", errno); + //don't support + *supt = 0; + return ret; + } + LOGI("isRDSsupport Success,[support=%d]\n", support); + *supt = support; + return ret; +} + +int COM_pre_search(int fd) +{ + fm_s32 ret = 0; + ret = ioctl(fd, FM_IOCTL_PRE_SEARCH, 0); + LOGD("COM_pre_search:%d\n",ret); + return ret; +} + +int COM_restore_search(int fd) +{ + fm_s32 ret = 0; + ret = ioctl(fd, FM_IOCTL_RESTORE_SEARCH, 0); + LOGD("COM_restore_search:%d\n",ret); + return ret; +} + +/*soft mute tune function, usually for sw scan implement or CQI log tool*/ +int COM_Soft_Mute_Tune(int fd, fm_softmute_tune_t *para) +{ + fm_s32 ret = 0; + //fm_s32 RSSI = 0, PAMD = 0,MR = 0, ATDC = 0; + //fm_u32 PRX = 0; + //fm_u16 softmuteGainLvl = 0; + fm_softmute_tune_t value; + + value.freq = para->freq; + ret = ioctl(fd, FM_IOCTL_SOFT_MUTE_TUNE, &value); + if (ret) { + LOGE("FM soft mute tune faild:%d\n",ret); + return ret; + } +#if 0 + LOGD("Raw data of soft mute tune[%d]: RSSI:[%x]PAMD:[%x]MR:[%x]ATDC:[%x]PRX:[%x]SMG:[%x]",para->freq,value.RSSI,value.PAMD,value.MR,value.ATDC,value.PRX,value.SMG); + RSSI = ((value.RSSI & 0x03FF) >= 512) ? ((value.RSSI & 0x03FF) - 1024) : (value.RSSI & 0x03FF); + PAMD = ((value.PAMD & 0xFF) >= 128) ? ((value.PAMD & 0x00FF) - 256) : (value.PAMD & 0x00FF); + MR = ((value.MR & 0x01FF) >= 256) ? ((value.MR & 0x01FF) - 512) : (value.MR & 0x01FF); + ATDC =((value.ATDC & 0x0FFF) >= 2048) ? ((value.ATDC & 0x0FFF) - 4096) : (value.ATDC & 0x0FFF); + if (ATDC < 0) { + ATDC = (~(ATDC)) - 1;//Get abs value of ATDC + } + PRX = (value.PRX & 0x00FF); + softmuteGainLvl = value.SMG; + //check if the channel is valid according to each CQIs + if ((RSSI >= RSSI_TH) + && (PAMD <= PAMD_TH) + && (ATDC <= ATDC_TH) + && (MR >= MR_TH) + && (PRX >= PRX_TH) + && (softmuteGainLvl <= softMuteGainTH)) { + para->valid = fm_true; + } else { + para->valid = fm_false; + } +#endif + para->valid = value.valid; + para->rssi = value.rssi; + //LOGI("soft mute tune[%d] valid[%d]: RSSI:[%d]PAMD:[%d]MR:[%d]ATDC:[%d]PRX:[%d]SMG:[%d]",para->freq,para->valid,RSSI,PAMD,MR,ATDC,PRX,softmuteGainLvl); + return 0; +} + +int COM_get_cqi(int fd, int num, char *buf, int buf_len) +{ + int ret; + struct fm_cqi_req cqi_req; + + //check buf + num = (num > CQI_CH_NUM_MAX) ? CQI_CH_NUM_MAX : num; + num = (num < CQI_CH_NUM_MIN) ? CQI_CH_NUM_MIN : num; + cqi_req.ch_num = (uint16_t)num; + cqi_req.buf_size = cqi_req.ch_num * sizeof(struct fm_cqi); + if (!buf || (buf_len < cqi_req.buf_size)) { + LOGE("get cqi, invalid buf\n"); + return -1; + } + cqi_req.cqi_buf = buf; + + //get cqi from driver + ret = ioctl(fd, FM_IOCTL_CQI_GET, &cqi_req); + if (ret < 0) { + LOGE("get cqi, failed %d\n", ret); + return -1; + } + + return 0; +} + +int COM_turn_on_off_rds(int fd, int onoff) +{ + int ret = 0; + uint16_t rds_on = -1; + + LOGD("Rdsset start\n"); + if (onoff == FMR_RDS_ON) { + rds_on = 1; + ret = ioctl(fd, FM_IOCTL_RDS_ONOFF, &rds_on); + if (ret) { + LOGE("FM_IOCTL_RDS_ON failed\n"); + return ret; + } + LOGD("Rdsset Success,[rds_on=%d]\n", rds_on); + } else { + rds_on = 0; + ret = ioctl(fd, FM_IOCTL_RDS_ONOFF, &rds_on); + if (ret) { + LOGE("FM_IOCTL_RDS_OFF failed\n"); + return ret; + } + LOGD("Rdsset Success,[rds_on=%d]\n", rds_on); + } + return ret; +} + +int COM_get_chip_id(int fd, int *chipid) +{ + int ret = 0; + uint16_t tmp = 0; + + FMR_ASSERT(chipid); + + ret = ioctl(fd, FM_IOCTL_GETCHIPID, &tmp); + *chipid = (int)tmp; + if (ret) { + LOGE("%s, failed\n", __func__); + } + LOGD("%s, [fd=%d] [chipid=%x] [ret=%d]\n", __func__, fd, *chipid, ret); + return ret; +} + +int COM_read_rds_data(int fd, RDSData_Struct *rds, uint16_t *rds_status) +{ + int ret = 0; + uint16_t event_status; + //char tmp_ps[9] = {0}; + //char tmp_rt[65] = { 0 }; + + FMR_ASSERT(rds); + FMR_ASSERT(rds_status); + + if (read(fd, rds, sizeof(RDSData_Struct)) == sizeof(RDSData_Struct)) { + event_status = rds->event_status; + //memcpy(tmp_ps, &rds->PS_Data.PS[3][0], 8); + //memcpy(tmp_rt, &rds->RT_Data.TextData[3][0], 64); + LOGI("event_status = 0x%x\n", event_status); + //memset(tmp_ps, 0, 9); + //memset(tmp_rt, 0, 65); + *rds_status = event_status; + return ret; + } else { + //LOGE("readrds get no event\n"); + ret = -ERR_RDS_NO_DATA; + } + return ret; +} + +int COM_active_af(int fd, RDSData_Struct *rds, int band, uint16_t cur_freq, uint16_t *ret_freq) +{ + int ret = 0; + int i = 0; + struct fm_tune_parm parm; + uint16_t rds_on = 0; + uint16_t set_freq, sw_freq, org_freq, PAMD_Value, AF_PAMD_LBound, AF_PAMD_HBound; + uint16_t PAMD_Level[25]; + uint16_t PAMD_DB_TBL[5] = {// 5dB, 10dB, 15dB, 20dB, 25dB, + // 13, 17, 21, 25, 29}; + 8, 12, 15, 18, 20}; + FMR_ASSERT(rds); + FMR_ASSERT(ret_freq); + + sw_freq = cur_freq; //current freq + org_freq = cur_freq; + parm.band = band; + parm.freq = sw_freq; + parm.hilo = FM_AUTO_HILO_OFF; + parm.space = FM_SPACE_DEFAULT; + + if (!(rds->event_status&RDS_EVENT_AF)) { + LOGE("activeAF failed\n"); + *ret_freq = 0; + ret = -ERR_RDS_NO_DATA; + return ret; + } + + AF_PAMD_LBound = PAMD_DB_TBL[0]; //5dB + AF_PAMD_HBound = PAMD_DB_TBL[1]; //15dB + ioctl(fd, FM_IOCTL_GETCURPAMD, &PAMD_Value); + LOGI("current_freq=%d,PAMD_Value=%d\n", cur_freq, PAMD_Value); + + if (PAMD_Value < AF_PAMD_LBound) { + rds_on = 0; + ioctl(fd, FM_IOCTL_RDS_ONOFF, &rds_on); + //make sure rds->AF_Data.AF_Num is valid + rds->AF_Data.AF_Num = (rds->AF_Data.AF_Num > 25)? 25 : rds->AF_Data.AF_Num; + for (i=0; i<rds->AF_Data.AF_Num; i++) { + set_freq = rds->AF_Data.AF[1][i]; //method A or B + if (set_freq != org_freq) { + parm.freq = set_freq; + ioctl(fd, FM_IOCTL_TUNE, &parm); + usleep(250*1000); + ioctl(fd, FM_IOCTL_GETCURPAMD, &PAMD_Level[i]); + LOGI("next_freq=%d,PAMD_Level=%d\n", parm.freq, PAMD_Level[i]); + if (PAMD_Level[i] > PAMD_Value) { + PAMD_Value = PAMD_Level[i]; + sw_freq = set_freq; + } + } + } + LOGI("PAMD_Value=%d, sw_freq=%d\n", PAMD_Value, sw_freq); + if ((PAMD_Value > AF_PAMD_HBound)&&(sw_freq != 0)) { + parm.freq = sw_freq; + ioctl(fd, FM_IOCTL_TUNE, &parm); + cur_freq = parm.freq; + } else { + parm.freq = org_freq; + ioctl(fd, FM_IOCTL_TUNE, &parm); + cur_freq = parm.freq; + } + rds_on = 1; + ioctl(fd, FM_IOCTL_RDS_ONOFF, &rds_on); + } else { + LOGD("RDS_EVENT_AF old freq:%d\n", org_freq); + } + *ret_freq = cur_freq; + + return ret; +} + +int COM_ana_switch(int fd, int antenna) +{ + int ret = 0; + + ret = ioctl(fd, FM_IOCTL_ANA_SWITCH, &antenna); + if (ret < 0) { + LOGE("%s: fail, ret = %d\n", __func__, ret); + } + + LOGD("%s: [ret = %d]\n", __func__, ret); + return ret; +} + +/* COM_is_dese_chan -- check if gived channel is a de-sense channel or not + * @fd - fd of "dev/fm" + * @freq - gived channel + * return value: 0, not a dese chan; 1, a dese chan; else error NO. + */ +int COM_is_dese_chan(int fd, int freq) +{ + int ret = 0; + int tmp = freq; + + ret = ioctl(fd, FM_IOCTL_IS_DESE_CHAN, &freq); + if (ret < 0) { + LOGE("%s, failed,ret=%d\n", __func__,ret); + return ret; + } else { + LOGD("[fd=%d] %d --> dese=%d\n", fd, tmp, freq); + return freq; + } +} + +/* COM_desense_check -- check if gived channel is a de-sense channel or not + * @fd - fd of "dev/fm" + * @freq - gived channel + * @rssi-freq's rssi + * return value: 0, is desense channel and rssi is less than threshold; 1, not desense channel or it is but rssi is more than threshold. + */ +int COM_desense_check(int fd, int freq, int rssi) +{ + int ret = 0; + fm_desense_check_t parm; + + parm.freq = freq; + parm.rssi = rssi; + ret = ioctl(fd, FM_IOCTL_DESENSE_CHECK, &parm); + if (ret < 0) { + LOGE("%s, failed,ret=%d\n", __func__,ret); + return ret; + } else { + LOGD("[fd=%d] %d --> dese=%d\n", fd,freq,ret); + return ret; + } +} + +void FM_interface_init(struct fm_cbk_tbl *cbk_tbl) +{ + //Basic functions. + cbk_tbl->open_dev = COM_open_dev; + cbk_tbl->close_dev = COM_close_dev; + cbk_tbl->pwr_up = COM_pwr_up; + cbk_tbl->pwr_down = COM_pwr_down; + cbk_tbl->tune = COM_tune; + cbk_tbl->set_mute = COM_set_mute; + cbk_tbl->is_rdsrx_support = COM_is_rdsrx_support; + cbk_tbl->turn_on_off_rds = COM_turn_on_off_rds; + cbk_tbl->get_chip_id = COM_get_chip_id; + //For RDS RX. + cbk_tbl->read_rds_data = COM_read_rds_data; + cbk_tbl->get_ps = COM_get_ps; + cbk_tbl->get_rt = COM_get_rt; + cbk_tbl->active_af = COM_active_af; + //FM short antenna + cbk_tbl->ana_switch = COM_ana_switch; + cbk_tbl->desense_check = COM_desense_check; + //soft mute tune + cbk_tbl->soft_mute_tune = COM_Soft_Mute_Tune; + cbk_tbl->pre_search = COM_pre_search; + cbk_tbl->restore_search = COM_restore_search; + return; +} + diff --git a/jni/fmr/fmr.h b/jni/fmr/fmr.h new file mode 100644 index 0000000..4af68e3 --- /dev/null +++ b/jni/fmr/fmr.h @@ -0,0 +1,230 @@ +/* + * 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. + */ + +#ifndef __FMR_H__ +#define __FMR_H__ + +#include <jni.h> +#include <utils/Log.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <termios.h> +#include <pthread.h> +#include <linux/fm.h> +#include <signal.h> +#include <errno.h> +#include <dlfcn.h> + +#define FM_LIB_USE_XLOG + +#ifdef FM_LIB_USE_XLOG +#include <cutils/xlog.h> +#undef LOGV +#define LOGV(...) XLOGV(__VA_ARGS__) +#undef LOGD +#define LOGD(...) XLOGD(__VA_ARGS__) +#undef LOGI +#define LOGI(...) XLOGI(__VA_ARGS__) +#undef LOGW +#define LOGW(...) XLOGW(__VA_ARGS__) +#undef LOGE +#define LOGE(...) XLOGE(__VA_ARGS__) +#endif + +#define CUST_LIB_NAME "libfmcust.so" +#define FM_DEV_NAME "/dev/fm" + +#define FM_RDS_PS_LEN 8 + +struct fm_fake_channel +{ + int freq; + int rssi_th; + int reserve; +}; + +struct fm_fake_channel_t +{ + int size; + struct fm_fake_channel *chan; +}; + +struct CUST_cfg_ds +{ + int16_t chip; + int32_t band; + int32_t low_band; + int32_t high_band; + int32_t seek_space; + int32_t max_scan_num; + int32_t seek_lev; + int32_t scan_sort; + int32_t short_ana_sup; + int32_t rssi_th_l2; + struct fm_fake_channel_t *fake_chan; +}; + +struct fm_cbk_tbl { + //Basic functions. + int (*open_dev)(const char *pname, int *fd); + int (*close_dev)(int fd); + int (*pwr_up)(int fd, int band, int freq); + int (*pwr_down)(int fd, int type); + int (*seek)(int fd, int *freq, int band, int dir, int lev); + int (*scan)(int fd, uint16_t *tbl, int *num, int band, int sort); + int (*stop_scan)(int fd); + int (*tune)(int fd, int freq, int band); + int (*set_mute)(int fd, int mute); + int (*is_rdsrx_support)(int fd, int *supt); + int (*turn_on_off_rds)(int fd, int onoff); + int (*get_chip_id)(int fd, int *chipid); + //FOR RDS RX. + int (*read_rds_data)(int fd, RDSData_Struct *rds, uint16_t *rds_status); + int (*get_ps)(int fd, RDSData_Struct *rds, uint8_t **ps, int *ps_len); + int (*get_rt)(int fd, RDSData_Struct *rds, uint8_t **rt, int *rt_len); + int (*active_af)(int fd, RDSData_Struct *rds, int band, uint16_t cur_freq, uint16_t *ret_freq); + //FM long/short antenna switch + int (*ana_switch)(int fd, int antenna); + int (*soft_mute_tune)(int fd, fm_softmute_tune_t *para); + int (*desense_check)(int fd, int freq, int rssi); + int (*pre_search)(int fd); + int (*restore_search)(int fd); +}; + +typedef int (*CUST_func_type)(struct CUST_cfg_ds *); +typedef void (*init_func_type)(struct fm_cbk_tbl *); + +struct fmr_ds { + int fd; + int err; + uint16_t cur_freq; + uint16_t backup_freq; + void *priv; + void *custom_handler; + struct CUST_cfg_ds cfg_data; + struct fm_cbk_tbl tbl; + CUST_func_type get_cfg; + void *init_handler; + init_func_type init_func; + RDSData_Struct rds; + struct fm_hw_info hw_info; + fm_bool scan_stop; +}; + +enum fmr_err_em { + ERR_SUCCESS = 1000, // kernel error begin at here + ERR_INVALID_BUF, + ERR_INVALID_PARA, + ERR_STP, + ERR_GET_MUTEX, + ERR_FW_NORES, + ERR_RDS_CRC, + ERR_INVALID_FD, // native error begin at here + ERR_UNSUPPORT_CHIP, + ERR_LD_LIB, + ERR_FIND_CUST_FNUC, + ERR_UNINIT, + ERR_NO_MORE_IDX, + ERR_RDS_NO_DATA, + ERR_UNSUPT_SHORTANA, + ERR_MAX +}; + +enum fmr_rds_onoff +{ + FMR_RDS_ON, + FMR_RDS_OFF, + FMR_MAX +}; + +typedef enum +{ + FM_LONG_ANA = 0, + FM_SHORT_ANA +}fm_antenna_type; + + +#define CQI_CH_NUM_MAX 255 +#define CQI_CH_NUM_MIN 0 + + +/****************** Function declaration ******************/ +//fmr_err.cpp +char *FMR_strerr(); +void FMR_seterr(int err); + +//fmr_core.cpp +int FMR_init(void); +int FMR_get_cfgs(int idx); +int FMR_open_dev(int idx); +int FMR_close_dev(int idx); +int FMR_pwr_up(int idx, int freq); +int FMR_pwr_down(int idx, int type); +int FMR_seek(int idx, int start_freq, int dir, int *ret_freq); +int FMR_scan(int idx, uint16_t *tbl, int *num); +int FMR_stop_scan(int idx); +int FMR_tune(int idx, int freq); +int FMR_set_mute(int idx, int mute); +int FMR_is_rdsrx_support(int idx, int *supt); +int FMR_turn_on_off_rds(int idx, int onoff); +int FMR_get_chip_id(int idx, int *chipid); +int FMR_read_rds_data(int idx, uint16_t *rds_status); +int FMR_get_ps(int idx, uint8_t **ps, int *ps_len); +int FMR_get_rssi(int idx, int *rssi); +int FMR_get_rt(int idx, uint8_t **rt, int *rt_len); +int FMR_active_af(int idx, uint16_t *ret_freq); + +int FMR_ana_switch(int idx, int antenna); +int FMR_Pre_Search(int idx); +int FMR_Restore_Search(int idx); + +//common part +int COM_open_dev(const char *pname, int *fd); +int COM_close_dev(int fd); +int COM_pwr_up(int fd, int band, int freq); +int COM_pwr_down(int fd, int type); +int COM_seek(int fd, int *freq, int band, int dir, int lev); +int COM_Soft_Mute_Tune(int fd, fm_softmute_tune_t *para); + +int COM_stop_scan(int fd); +int COM_tune(int fd, int freq, int band); +int COM_set_mute(int fd, int mute); +int COM_is_rdsrx_support(int fd, int *supt); +int COM_turn_on_off_rds(int fd, int onoff); +int COM_get_chip_id(int fd, int *chipid); +int COM_read_rds_data(int fd, RDSData_Struct *rds, uint16_t *rds_status); +int COM_get_ps(int fd, RDSData_Struct *rds, uint8_t **ps, int *ps_len); +int COM_get_rt(int fd, RDSData_Struct *rds, uint8_t **rt, int *rt_len); +int COM_active_af(int fd, RDSData_Struct *rds, int band, uint16_t cur_freq, uint16_t *ret_freq); + +int COM_ana_switch(int fd, int antenna); +int COM_desense_check(int fd, int freq, int rssi); +int COM_pre_search(int fd); +int COM_restore_search(int fd); +void FM_interface_init(struct fm_cbk_tbl *cbk_tbl); + +#define FMR_ASSERT(a) { \ + if ((a) == NULL) { \ + LOGE("%s,invalid buf\n", __func__);\ + return -ERR_INVALID_BUF; \ + } \ +} +#endif diff --git a/jni/fmr/fmr_core.cpp b/jni/fmr/fmr_core.cpp new file mode 100644 index 0000000..10c2511 --- /dev/null +++ b/jni/fmr/fmr_core.cpp @@ -0,0 +1,843 @@ +/* + * 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; +} + diff --git a/jni/fmr/fmr_err.cpp b/jni/fmr/fmr_err.cpp new file mode 100644 index 0000000..ffbf3cc --- /dev/null +++ b/jni/fmr/fmr_err.cpp @@ -0,0 +1,39 @@ +/* + * 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. + */ + +#include "fmr.h" + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "FMLIB_ERR" + +#define FMR_ERR_BASE -0x100 + +static int fmr_err = 0; +static char tmp[20] = {0}; + +char *FMR_strerr() +{ + sprintf(tmp, "%d", fmr_err); + + return tmp; +} + +void FMR_seterr(int err) +{ + fmr_err = err; +} diff --git a/jni/fmr/libfm_jni.cpp b/jni/fmr/libfm_jni.cpp new file mode 100644 index 0000000..b772e7f --- /dev/null +++ b/jni/fmr/libfm_jni.cpp @@ -0,0 +1,437 @@ +/* + * 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. + */ + +#include <jni.h> +#include "fmr.h" + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "FMLIB_JNI" + +static int g_idx = -1; +extern struct fmr_ds fmr_data; + +jboolean openDev(JNIEnv *env, jobject thiz) +{ + int ret = 0; + + ret = FMR_open_dev(g_idx); // if success, then ret = 0; else ret < 0 + + LOGD("%s, [ret=%d]\n", __func__, ret); + return ret?JNI_FALSE:JNI_TRUE; +} + +jboolean closeDev(JNIEnv *env, jobject thiz) +{ + int ret = 0; + + ret = FMR_close_dev(g_idx); + + LOGD("%s, [ret=%d]\n", __func__, ret); + return ret?JNI_FALSE:JNI_TRUE; +} + +jboolean powerUp(JNIEnv *env, jobject thiz, jfloat freq) +{ + int ret = 0; + int tmp_freq; + + LOGI("%s, [freq=%d]\n", __func__, (int)freq); + tmp_freq = (int)(freq * 10); //Eg, 87.5 * 10 --> 875 + ret = FMR_pwr_up(g_idx, tmp_freq); + + LOGD("%s, [ret=%d]\n", __func__, ret); + return ret?JNI_FALSE:JNI_TRUE; +} + +jboolean powerDown(JNIEnv *env, jobject thiz, jint type) +{ + int ret = 0; + + ret = FMR_pwr_down(g_idx, type); + + LOGD("%s, [ret=%d]\n", __func__, ret); + return ret?JNI_FALSE:JNI_TRUE; +} + +jboolean tune(JNIEnv *env, jobject thiz, jfloat freq) +{ + int ret = 0; + int tmp_freq; + + tmp_freq = (int)(freq * 10); //Eg, 87.5 * 10 --> 875 + ret = FMR_tune(g_idx, tmp_freq); + + LOGD("%s, [ret=%d]\n", __func__, ret); + return ret?JNI_FALSE:JNI_TRUE; +} + +jfloat seek(JNIEnv *env, jobject thiz, jfloat freq, jboolean isUp) //jboolean isUp; +{ + int ret = 0; + int tmp_freq; + int ret_freq; + float val; + + tmp_freq = (int)(freq * 100); //Eg, 87.55 * 100 --> 8755 + ret = FMR_set_mute(g_idx, 1); + if (ret) { + LOGE("%s, error, [ret=%d]\n", __func__, ret); + } + LOGD("%s, [mute] [ret=%d]\n", __func__, ret); + + ret = FMR_seek(g_idx, tmp_freq, (int)isUp, &ret_freq); + if (ret) { + ret_freq = tmp_freq; //seek error, so use original freq + } + + LOGD("%s, [freq=%d] [ret=%d]\n", __func__, ret_freq, ret); + + val = (float)ret_freq/100; //Eg, 8755 / 100 --> 87.55 + + return val; +} + +jshortArray autoScan(JNIEnv *env, jobject thiz) +{ +#define FM_SCAN_CH_SIZE_MAX 200 + int ret = 0; + jshortArray scanChlarray; + int chl_cnt = FM_SCAN_CH_SIZE_MAX; + uint16_t ScanTBL[FM_SCAN_CH_SIZE_MAX]; + + LOGI("%s, [tbl=%p]\n", __func__, ScanTBL); + FMR_Pre_Search(g_idx); + ret = FMR_scan(g_idx, ScanTBL, &chl_cnt); + if (ret < 0) { + LOGE("scan failed!\n"); + scanChlarray = NULL; + goto out; + } + if (chl_cnt > 0) { + scanChlarray = env->NewShortArray(chl_cnt); + env->SetShortArrayRegion(scanChlarray, 0, chl_cnt, (const jshort*)&ScanTBL[0]); + } else { + LOGE("cnt error, [cnt=%d]\n", chl_cnt); + scanChlarray = NULL; + } + FMR_Restore_Search(g_idx); + + if (fmr_data.scan_stop == fm_true) { + ret = FMR_tune(g_idx, fmr_data.cur_freq); + LOGI("scan stop!!! tune ret=%d",ret); + scanChlarray = NULL; + ret = -1; + } + +out: + LOGD("%s, [cnt=%d] [ret=%d]\n", __func__, chl_cnt, ret); + return scanChlarray; +} + +jshort readRds(JNIEnv *env, jobject thiz) +{ + int ret = 0; + uint16_t status = 0; + + ret = FMR_read_rds_data(g_idx, &status); + + if (ret) { + //LOGE("%s,status = 0,[ret=%d]\n", __func__, ret); + status = 0; //there's no event or some error happened + } + + return status; +} + +jbyteArray getPs(JNIEnv *env, jobject thiz) +{ + int ret = 0; + jbyteArray PSname; + uint8_t *ps = NULL; + int ps_len = 0; + + ret = FMR_get_ps(g_idx, &ps, &ps_len); + if (ret) { + LOGE("%s, error, [ret=%d]\n", __func__, ret); + return NULL; + } + PSname = env->NewByteArray(ps_len); + env->SetByteArrayRegion(PSname, 0, ps_len, (const jbyte*)ps); + LOGD("%s, [ret=%d]\n", __func__, ret); + return PSname; +} + +jbyteArray getLrText(JNIEnv *env, jobject thiz) +{ + int ret = 0; + jbyteArray LastRadioText; + uint8_t *rt = NULL; + int rt_len = 0; + + ret = FMR_get_rt(g_idx, &rt, &rt_len); + if (ret) { + LOGE("%s, error, [ret=%d]\n", __func__, ret); + return NULL; + } + LastRadioText = env->NewByteArray(rt_len); + env->SetByteArrayRegion(LastRadioText, 0, rt_len, (const jbyte*)rt); + LOGD("%s, [ret=%d]\n", __func__, ret); + return LastRadioText; +} + +jshort activeAf(JNIEnv *env, jobject thiz) +{ + int ret = 0; + jshort ret_freq = 0; + + ret = FMR_active_af(g_idx, (uint16_t*)&ret_freq); + if (ret) { + LOGE("%s, error, [ret=%d]\n", __func__, ret); + return 0; + } + LOGD("%s, [ret=%d]\n", __func__, ret); + return ret_freq; +} + +jshortArray getAFList(JNIEnv *env, jobject thiz) +{ + int ret = 0; + jshortArray AFList; + char *af = NULL; + int af_len = 0; + + //ret = FMR_get_af(g_idx, &af, &af_len); // If need, we should implemate this API + if (ret) { + LOGE("%s, error, [ret=%d]\n", __func__, ret); + return NULL; + } + AFList = env->NewShortArray(af_len); + env->SetShortArrayRegion(AFList, 0, af_len, (const jshort*)af); + LOGD("%s, [ret=%d]\n", __func__, ret); + return AFList; +} + +jint setRds(JNIEnv *env, jobject thiz, jboolean rdson) +{ + int ret = 0; + int onoff = -1; + + if (rdson == JNI_TRUE) { + onoff = FMR_RDS_ON; + } else { + onoff = FMR_RDS_OFF; + } + ret = FMR_turn_on_off_rds(g_idx, onoff); + if (ret) { + LOGE("%s, error, [ret=%d]\n", __func__, ret); + } + LOGD("%s, [onoff=%d] [ret=%d]\n", __func__, onoff, ret); + return ret?JNI_FALSE:JNI_TRUE; +} + +jboolean stopScan(JNIEnv *env, jobject thiz) +{ + int ret = 0; + + ret = FMR_stop_scan(g_idx); + if (ret) { + LOGE("%s, error, [ret=%d]\n", __func__, ret); + } + LOGD("%s, [ret=%d]\n", __func__, ret); + return ret?JNI_FALSE:JNI_TRUE; +} + +jint setMute(JNIEnv *env, jobject thiz, jboolean mute) +{ + int ret = 0; + + ret = FMR_set_mute(g_idx, (int)mute); + if (ret) { + LOGE("%s, error, [ret=%d]\n", __func__, ret); + } + LOGD("%s, [mute=%d] [ret=%d]\n", __func__, (int)mute, ret); + return ret?JNI_FALSE:JNI_TRUE; +} + +/****************************************** + * Inquiry if RDS is support in driver. + * Parameter: + * None + *Return Value: + * 1: support + * 0: NOT support + * -1: error + ******************************************/ +jint isRdsSupport(JNIEnv *env, jobject thiz) +{ + int ret = 0; + int supt = -1; + + ret = FMR_is_rdsrx_support(g_idx, &supt); + if (ret) { + LOGE("%s, error, [ret=%d]\n", __func__, ret); + } + LOGD("%s, [supt=%d] [ret=%d]\n", __func__, supt, ret); + return supt; +} + +/****************************************** + * SwitchAntenna + * Parameter: + * antenna: + 0 : switch to long antenna + 1: switch to short antenna + *Return Value: + * 0: Success + * 1: Failed + * 2: Not support + ******************************************/ +jint switchAntenna(JNIEnv *env, jobject thiz, jint antenna) +{ + int ret = 0; + jint jret = 0; + int ana = -1; + + if (0 == antenna) { + ana = FM_LONG_ANA; + } else if (1 == antenna) { + ana = FM_SHORT_ANA; + } else { + LOGE("%s:fail, para error\n", __func__); + jret = JNI_FALSE; + goto out; + } + ret = FMR_ana_switch(g_idx, ana); + if (ret == -ERR_UNSUPT_SHORTANA) { + LOGW("Not support switchAntenna\n"); + jret = 2; + } else if (ret) { + LOGE("switchAntenna(), error\n"); + jret = 1; + } else { + jret = 0; + } +out: + LOGD("%s: [antenna=%d] [ret=%d]\n", __func__, ana, ret); + return jret; +} + +static const char *classPathNameRx = "com/android/fmradio/FmNative"; + +static JNINativeMethod methodsRx[] = { + {"openDev", "()Z", (void*)openDev }, //1 + {"closeDev", "()Z", (void*)closeDev }, //2 + {"powerUp", "(F)Z", (void*)powerUp }, //3 + {"powerDown", "(I)Z", (void*)powerDown }, //4 + {"tune", "(F)Z", (void*)tune }, //5 + {"seek", "(FZ)F", (void*)seek }, //6 + {"autoScan", "()[S", (void*)autoScan }, //7 + {"stopScan", "()Z", (void*)stopScan }, //8 + {"setRds", "(Z)I", (void*)setRds }, //10 + {"readRds", "()S", (void*)readRds }, //11 will pending here for get event status + {"getPs", "()[B", (void*)getPs }, //12 + {"getLrText", "()[B", (void*)getLrText}, //13 + {"activeAf", "()S", (void*)activeAf}, //14 + {"setMute", "(Z)I", (void*)setMute}, //15 + {"isRdsSupport", "()I", (void*)isRdsSupport}, //16 + {"switchAntenna", "(I)I", (void*)switchAntenna}, //17 +}; + +/* + * Register several native methods for one class. + */ +static jint registerNativeMethods(JNIEnv* env, const char* className, + JNINativeMethod* gMethods, int numMethods) +{ + jclass clazz; + + clazz = env->FindClass(className); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + if (clazz == NULL) { + LOGE("Native registration unable to find class '%s'", className); + return JNI_FALSE; + } + if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) { + LOGE("RegisterNatives failed for '%s'", className); + return JNI_FALSE; + } + + LOGD("%s, success\n", __func__); + return JNI_TRUE; +} + +/* + * Register native methods for all classes we know about. + * + * returns JNI_TRUE on success. + */ +static jint registerNatives(JNIEnv* env) +{ + jint ret = JNI_FALSE; + + if (registerNativeMethods(env, classPathNameRx,methodsRx, + sizeof(methodsRx) / sizeof(methodsRx[0]))) { + ret = JNI_TRUE; + } + + LOGD("%s, done\n", __func__); + return ret; +} + +// ---------------------------------------------------------------------------- + +/* + * This is called by the VM when the shared library is first loaded. + */ + +typedef union { + JNIEnv* env; + void* venv; +} UnionJNIEnvToVoid; + +jint JNI_OnLoad(JavaVM* vm, void* reserved) +{ + UnionJNIEnvToVoid uenv; + uenv.venv = NULL; + jint result = -1; + JNIEnv* env = NULL; + + LOGI("JNI_OnLoad"); + + if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) { + LOGE("ERROR: GetEnv failed"); + goto fail; + } + env = uenv.env; + + if (registerNatives(env) != JNI_TRUE) { + LOGE("ERROR: registerNatives failed"); + goto fail; + } + + if ((g_idx = FMR_init()) < 0) { + goto fail; + } + result = JNI_VERSION_1_4; + +fail: + return result; +} |