[FM] Move FM Radio JNI
The CL is to add jni folder in packages/apps/FMRadio.
https://partner-android-review.git.corp.google.com/#/c/193524/
Bug 18632972
Change-Id: Ib98143af4870eedd8752d28970ff2056577225db
Signed-off-by: Benson Huang <benson.huang@mediatek.com>
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..1700b4e
--- /dev/null
+++ b/jni/fmr/Android.mk
@@ -0,0 +1,35 @@
+# 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/fm.h b/jni/fmr/fm.h
new file mode 100644
index 0000000..736b222
--- /dev/null
+++ b/jni/fmr/fm.h
@@ -0,0 +1,547 @@
+/*
+ * 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 __FM_H__
+#define __FM_H__
+
+#include <linux/ioctl.h>
+#include <linux/time.h>
+
+typedef signed char fm_s8;
+typedef signed short fm_s16;
+typedef signed int fm_s32;
+typedef signed long long fm_s64;
+typedef unsigned char fm_u8;
+typedef unsigned short fm_u16;
+typedef unsigned int fm_u32;
+typedef unsigned long long fm_u64;
+typedef enum fm_bool {
+ fm_false = 0,
+ fm_true = 1
+} fm_bool;
+
+// scan sort algorithm
+enum {
+ FM_SCAN_SORT_NON = 0,
+ FM_SCAN_SORT_UP,
+ FM_SCAN_SORT_DOWN,
+ FM_SCAN_SORT_MAX
+};
+
+// scan methods
+enum {
+ FM_SCAN_SEL_HW = 0, // select hardware scan, advantage: fast
+ FM_SCAN_SEL_SW, // select software scan, advantage: more accurate
+ FM_SCAN_SEL_MAX
+};
+
+//*****************************************************************************************
+//***********************************FM config for customer *******************************
+//*****************************************************************************************
+#define FMR_RSSI_TH_LONG 0x0301 // FM radio long antenna RSSI threshold(11.375dBuV)
+#define FMR_RSSI_TH_SHORT 0x02E0 // FM radio short antenna RSSI threshold(-1dBuV)
+#define FMR_CQI_TH 0x00E9 // FM radio Channel quality indicator threshold(0x0000~0x00FF)
+#define FMR_SEEK_SPACE 1 // FM radio seek space,1:100KHZ; 2:200KHZ
+#define FMR_SCAN_CH_SIZE 40 // FM radio scan max channel size
+#define FMR_BAND 1 // FM radio band, 1:87.5MHz~108.0MHz; 2:76.0MHz~90.0MHz;
+ // 3:76.0MHz~108.0MHz; 4:special
+#define FMR_BAND_FREQ_L 875 // FM radio special band low freq(Default 87.5MHz)
+#define FMR_BAND_FREQ_H 1080 // FM radio special band high freq(Default 108.0MHz)
+#define FM_SCAN_SORT_SELECT FM_SCAN_SORT_NON
+#define FM_SCAN_SELECT FM_SCAN_SEL_HW
+#define FM_SCAN_SOFT_MUTE_GAIN_TH 3 // soft-mute threshold when software scan, rang: 0~3,
+ // 0 means better audio quality but less channel
+#define FM_CHIP_DESE_RSSI_TH (-102) // rang: -102 ~ -72
+
+//*****************************************************************************************
+//***********************************FM config for engineer *******************************
+//*****************************************************************************************
+#define FMR_MR_TH 0x01BD // FM radio MR threshold
+#define ADDR_SCAN_TH 0xE0 // scan thrshold register
+#define ADDR_CQI_TH 0xE1 // scan CQI register
+//*****************************************************************************************
+
+#define FM_NAME "fm"
+#define FM_DEVICE_NAME "/dev/fm"
+
+// errno
+#define FM_SUCCESS 0
+#define FM_FAILED 1
+#define FM_EPARM 2
+#define FM_BADSTATUS 3
+#define FM_TUNE_FAILED 4
+#define FM_SEEK_FAILED 5
+#define FM_BUSY 6
+#define FM_SCAN_FAILED 7
+
+// band
+#define FM_BAND_UNKNOWN 0
+#define FM_BAND_UE 1 // US/Europe band 87.5MHz ~ 108MHz (DEFAULT)
+#define FM_BAND_JAPAN 2 // Japan band 76MHz ~ 90MHz
+#define FM_BAND_JAPANW 3 // Japan wideband 76MHZ ~ 108MHz
+#define FM_BAND_SPECIAL 4 // special band between 76MHZ and 108MHz
+#define FM_BAND_DEFAULT FM_BAND_UE
+
+#define FM_UE_FREQ_MIN 875
+#define FM_UE_FREQ_MAX 1080
+#define FM_JP_FREQ_MIN 760
+#define FM_JP_FREQ_MAX 1080
+#define FM_FREQ_MIN FMR_BAND_FREQ_L
+#define FM_FREQ_MAX FMR_BAND_FREQ_H
+#define FM_RAIDO_BAND FM_BAND_UE
+
+// space
+#define FM_SPACE_UNKNOWN 0
+#define FM_SPACE_100K 1
+#define FM_SPACE_200K 2
+#define FM_SPACE_50K 5
+#define FM_SPACE_DEFAULT FM_SPACE_100K
+
+#define FM_SEEK_SPACE FMR_SEEK_SPACE
+
+// max scan channel num
+#define FM_MAX_CHL_SIZE FMR_SCAN_CH_SIZE
+// auto HiLo
+#define FM_AUTO_HILO_OFF 0
+#define FM_AUTO_HILO_ON 1
+
+// seek direction
+#define FM_SEEK_UP 0
+#define FM_SEEK_DOWN 1
+
+// seek threshold
+#define FM_SEEKTH_LEVEL_DEFAULT 4
+
+struct fm_tune_parm {
+ uint8_t err;
+ uint8_t band;
+ uint8_t space;
+ uint8_t hilo;
+ uint16_t freq;
+};
+
+struct fm_seek_parm {
+ uint8_t err;
+ uint8_t band;
+ uint8_t space;
+ uint8_t hilo;
+ uint8_t seekdir;
+ uint8_t seekth;
+ uint16_t freq;
+};
+
+struct fm_scan_parm {
+ uint8_t err;
+ uint8_t band;
+ uint8_t space;
+ uint8_t hilo;
+ uint16_t freq;
+ uint16_t ScanTBL[16];
+ uint16_t ScanTBLSize;
+};
+
+struct fm_ch_rssi {
+ uint16_t freq;
+ int rssi;
+};
+
+enum fm_scan_cmd_t {
+ FM_SCAN_CMD_INIT = 0,
+ FM_SCAN_CMD_START,
+ FM_SCAN_CMD_GET_NUM,
+ FM_SCAN_CMD_GET_CH,
+ FM_SCAN_CMD_GET_RSSI,
+ FM_SCAN_CMD_GET_CH_RSSI,
+ FM_SCAN_CMD_MAX
+};
+
+struct fm_scan_t {
+ enum fm_scan_cmd_t cmd;
+ int ret; // 0, success; else error code
+ uint16_t lower; // lower band, Eg, 7600 -> 76.0Mhz
+ uint16_t upper; // upper band, Eg, 10800 -> 108.0Mhz
+ int space; // 5: 50KHz, 10: 100Khz, 20: 200Khz
+ int num; // valid channel number
+ void *priv;
+ int sr_size; // scan result buffer size in bytes
+ union {
+ uint16_t *ch_buf; // channel buffer
+ int *rssi_buf; // rssi buffer
+ struct fm_ch_rssi *ch_rssi_buf; //channel and RSSI buffer
+ } sr;
+};
+
+struct fm_seek_t {
+ int ret; // 0, success; else error code
+ uint16_t freq;
+ uint16_t lower; // lower band, Eg, 7600 -> 76.0Mhz
+ uint16_t upper; // upper band, Eg, 10800 -> 108.0Mhz
+ int space; // 5: 50KHz, 10: 100Khz, 20: 200Khz
+ int dir; // 0: up; 1: down
+ int th; // seek threshold in dbm(Eg, -95dbm)
+ void *priv;
+};
+
+struct fm_tune_t {
+ int ret; // 0, success; else error code
+ uint16_t freq;
+ uint16_t lower; // lower band, Eg, 7600 -> 76.0Mhz
+ uint16_t upper; // upper band, Eg, 10800 -> 108.0Mhz
+ int space; // 5: 50KHz, 10: 100Khz, 20: 200Khz
+ void *priv;
+};
+
+struct fm_softmute_tune_t {
+ fm_s32 rssi; // RSSI of current channel
+ fm_u16 freq; // current frequency
+ fm_bool valid; // current channel is valid(true) or not(false)
+};
+
+struct fm_rssi_req {
+ uint16_t num;
+ uint16_t read_cnt;
+ struct fm_ch_rssi cr[16*16];
+};
+
+struct fm_hw_info {
+ int chip_id;
+ int eco_ver;
+ int rom_ver;
+ int patch_ver;
+ int reserve;
+};
+
+struct fm_search_threshold_t {
+ fm_s32 th_type;// 0, RSSI. 1,desense RSSI. 2,SMG.
+ fm_s32 th_val; //threshold value
+ fm_s32 reserve;
+};
+
+#define NEED_DEF_RDS 1
+
+#if NEED_DEF_RDS
+//For RDS feature
+typedef struct {
+ uint8_t TP;
+ uint8_t TA;
+ uint8_t Music;
+ uint8_t Stereo;
+ uint8_t Artificial_Head;
+ uint8_t Compressed;
+ uint8_t Dynamic_PTY;
+ uint8_t Text_AB;
+ uint32_t flag_status;
+} RDSFlag_Struct;
+
+typedef struct {
+ uint16_t Month;
+ uint16_t Day;
+ uint16_t Year;
+ uint16_t Hour;
+ uint16_t Minute;
+ uint8_t Local_Time_offset_signbit;
+ uint8_t Local_Time_offset_half_hour;
+} CT_Struct;
+
+typedef struct {
+ int16_t AF_Num;
+ int16_t AF[2][25];
+ uint8_t Addr_Cnt;
+ uint8_t isMethod_A;
+ uint8_t isAFNum_Get;
+} AF_Info;
+
+typedef struct {
+ uint8_t PS[4][8];
+ uint8_t Addr_Cnt;
+} PS_Info;
+
+typedef struct {
+ uint8_t TextData[4][64];
+ uint8_t GetLength;
+ uint8_t isRTDisplay;
+ uint8_t TextLength;
+ uint8_t isTypeA;
+ uint8_t BufCnt;
+ uint16_t Addr_Cnt;
+} RT_Info;
+
+struct rds_raw_data {
+ int dirty; // indicate if the data changed or not
+ int len; // the data len form chip
+ uint8_t data[146];
+};
+
+struct rds_group_cnt {
+ unsigned long total;
+ unsigned long groupA[16]; // RDS groupA counter
+ unsigned long groupB[16]; // RDS groupB counter
+};
+
+enum rds_group_cnt_opcode {
+ RDS_GROUP_CNT_READ = 0,
+ RDS_GROUP_CNT_WRITE,
+ RDS_GROUP_CNT_RESET,
+ RDS_GROUP_CNT_MAX
+};
+
+struct rds_group_cnt_req {
+ int err;
+ enum rds_group_cnt_opcode op;
+ struct rds_group_cnt gc;
+};
+
+typedef struct {
+ CT_Struct CT;
+ RDSFlag_Struct RDSFlag;
+ uint16_t PI;
+ uint8_t Switch_TP;
+ uint8_t PTY;
+ AF_Info AF_Data;
+ AF_Info AFON_Data;
+ uint8_t Radio_Page_Code;
+ uint16_t Program_Item_Number_Code;
+ uint8_t Extend_Country_Code;
+ uint16_t Language_Code;
+ PS_Info PS_Data;
+ uint8_t PS_ON[8];
+ RT_Info RT_Data;
+ uint16_t event_status;
+ struct rds_group_cnt gc;
+} RDSData_Struct;
+
+//valid Rds Flag for notify
+typedef enum {
+ RDS_FLAG_IS_TP = 0x0001, // Program is a traffic program
+ RDS_FLAG_IS_TA = 0x0002, // Program currently broadcasts a traffic ann.
+ RDS_FLAG_IS_MUSIC = 0x0004, // Program currently broadcasts music
+ RDS_FLAG_IS_STEREO = 0x0008, // Program is transmitted in stereo
+ RDS_FLAG_IS_ARTIFICIAL_HEAD = 0x0010, // Program is an artificial head recording
+ RDS_FLAG_IS_COMPRESSED = 0x0020, // Program content is compressed
+ RDS_FLAG_IS_DYNAMIC_PTY = 0x0040, // Program type can change
+ RDS_FLAG_TEXT_AB = 0x0080 // If this flag changes state, a new radio text string begins
+} RdsFlag;
+
+typedef enum {
+ RDS_EVENT_FLAGS = 0x0001, // One of the RDS flags has changed state
+ RDS_EVENT_PI_CODE = 0x0002, // The program identification code has changed
+ RDS_EVENT_PTY_CODE = 0x0004, // The program type code has changed
+ RDS_EVENT_PROGRAMNAME = 0x0008, // The program name has changed
+ RDS_EVENT_UTCDATETIME = 0x0010, // A new UTC date/time is available
+ RDS_EVENT_LOCDATETIME = 0x0020, // A new local date/time is available
+ RDS_EVENT_LAST_RADIOTEXT = 0x0040, // A radio text string was completed
+ RDS_EVENT_AF = 0x0080, // Current Channel RF signal strength too weak, need do AF switch
+ RDS_EVENT_AF_LIST = 0x0100, // An alternative frequency list is ready
+ RDS_EVENT_AFON_LIST = 0x0200, // An alternative frequency list is ready
+ RDS_EVENT_TAON = 0x0400, // Other Network traffic announcement start
+ RDS_EVENT_TAON_OFF = 0x0800, // Other Network traffic announcement finished.
+ RDS_EVENT_RDS = 0x2000, // RDS Interrupt had arrived durint timer period
+ RDS_EVENT_NO_RDS = 0x4000, // RDS Interrupt not arrived durint timer period
+ RDS_EVENT_RDS_TIMER = 0x8000 // Timer for RDS Bler Check. ---- BLER block error rate
+} RdsEvent;
+#endif
+
+typedef enum {
+ FM_I2S_ON = 0,
+ FM_I2S_OFF,
+ FM_I2S_STATE_ERR
+} fm_i2s_state_e;
+
+typedef enum {
+ FM_I2S_MASTER = 0,
+ FM_I2S_SLAVE,
+ FM_I2S_MODE_ERR
+} fm_i2s_mode_e;
+
+typedef enum {
+ FM_I2S_32K = 0,
+ FM_I2S_44K,
+ FM_I2S_48K,
+ FM_I2S_SR_ERR
+} fm_i2s_sample_e;
+
+struct fm_i2s_setting {
+ int onoff;
+ int mode;
+ int sample;
+};
+
+typedef enum {
+ FM_RX = 0,
+ FM_TX = 1
+} FM_PWR_T;
+
+typedef struct fm_i2s_info {
+ int status; /* 0:FM_I2S_ON, 1:FM_I2S_OFF,2:error */
+ int mode; /* 0:FM_I2S_MASTER, 1:FM_I2S_SLAVE,2:error */
+ int rate; /* 0:FM_I2S_32K:32000,1:FM_I2S_44K:44100,2:FM_I2S_48K:48000,3:error */
+} fm_i2s_info_t;
+
+typedef enum {
+ FM_AUD_ANALOG = 0,
+ FM_AUD_I2S = 1,
+ FM_AUD_MRGIF = 2,
+ FM_AUD_ERR
+} fm_audio_path_e;
+
+typedef enum {
+ FM_I2S_PAD_CONN = 0,
+ FM_I2S_PAD_IO = 1,
+ FM_I2S_PAD_ERR
+} fm_i2s_pad_sel_e;
+
+typedef struct fm_audio_info {
+ fm_audio_path_e aud_path;
+ fm_i2s_info_t i2s_info;
+ fm_i2s_pad_sel_e i2s_pad;
+} fm_audio_info_t;
+
+struct fm_cqi {
+ int ch;
+ int rssi;
+ int reserve;
+};
+
+struct fm_cqi_req {
+ uint16_t ch_num;
+ int buf_size;
+ char *cqi_buf;
+};
+
+typedef struct {
+ int freq;
+ int rssi;
+} fm_desense_check_t;
+
+typedef struct {
+ uint16_t lower; // lower band, Eg, 7600 -> 76.0Mhz
+ uint16_t upper; // upper band, Eg, 10800 -> 108.0Mhz
+ int space; // 0x1: 50KHz, 0x2: 100Khz, 0x4: 200Khz
+ int cycle; // repeat times
+} fm_full_cqi_log_t;
+
+// ********** ***********FM IOCTL define start *******************************
+#define FM_IOC_MAGIC 0xf5
+
+#define FM_IOCTL_POWERUP _IOWR(FM_IOC_MAGIC, 0, struct fm_tune_parm*)
+#define FM_IOCTL_POWERDOWN _IOWR(FM_IOC_MAGIC, 1, int32_t*)
+#define FM_IOCTL_TUNE _IOWR(FM_IOC_MAGIC, 2, struct fm_tune_parm*)
+#define FM_IOCTL_SEEK _IOWR(FM_IOC_MAGIC, 3, struct fm_seek_parm*)
+#define FM_IOCTL_SETVOL _IOWR(FM_IOC_MAGIC, 4, uint32_t*)
+#define FM_IOCTL_GETVOL _IOWR(FM_IOC_MAGIC, 5, uint32_t*)
+#define FM_IOCTL_MUTE _IOWR(FM_IOC_MAGIC, 6, uint32_t*)
+#define FM_IOCTL_GETRSSI _IOWR(FM_IOC_MAGIC, 7, int32_t*)
+#define FM_IOCTL_SCAN _IOWR(FM_IOC_MAGIC, 8, struct fm_scan_parm*)
+#define FM_IOCTL_STOP_SCAN _IO(FM_IOC_MAGIC, 9)
+
+//IOCTL and struct for test
+#define FM_IOCTL_GETCHIPID _IOWR(FM_IOC_MAGIC, 10, uint16_t*)
+#define FM_IOCTL_EM_TEST _IOWR(FM_IOC_MAGIC, 11, struct fm_em_parm*)
+#define FM_IOCTL_RW_REG _IOWR(FM_IOC_MAGIC, 12, struct fm_ctl_parm*)
+#define FM_IOCTL_GETMONOSTERO _IOWR(FM_IOC_MAGIC, 13, uint16_t*)
+#define FM_IOCTL_GETCURPAMD _IOWR(FM_IOC_MAGIC, 14, uint16_t*)
+#define FM_IOCTL_GETGOODBCNT _IOWR(FM_IOC_MAGIC, 15, uint16_t*)
+#define FM_IOCTL_GETBADBNT _IOWR(FM_IOC_MAGIC, 16, uint16_t*)
+#define FM_IOCTL_GETBLERRATIO _IOWR(FM_IOC_MAGIC, 17, uint16_t*)
+
+//IOCTL for RDS
+#define FM_IOCTL_RDS_ONOFF _IOWR(FM_IOC_MAGIC, 18, uint16_t*)
+#define FM_IOCTL_RDS_SUPPORT _IOWR(FM_IOC_MAGIC, 19, int32_t*)
+
+#define FM_IOCTL_RDS_SIM_DATA _IOWR(FM_IOC_MAGIC, 23, uint32_t*)
+#define FM_IOCTL_IS_FM_POWERED_UP _IOWR(FM_IOC_MAGIC, 24, uint32_t*)
+
+//IOCTL for FM over BT
+#define FM_IOCTL_OVER_BT_ENABLE _IOWR(FM_IOC_MAGIC, 29, int32_t*)
+
+//IOCTL for FM ANTENNA SWITCH
+#define FM_IOCTL_ANA_SWITCH _IOWR(FM_IOC_MAGIC, 30, int32_t*)
+#define FM_IOCTL_GETCAPARRAY _IOWR(FM_IOC_MAGIC, 31, int32_t*)
+
+//IOCTL for FM I2S Setting
+#define FM_IOCTL_I2S_SETTING _IOWR(FM_IOC_MAGIC, 33, struct fm_i2s_setting*)
+
+#define FM_IOCTL_RDS_GROUPCNT _IOWR(FM_IOC_MAGIC, 34, struct rds_group_cnt_req*)
+#define FM_IOCTL_RDS_GET_LOG _IOWR(FM_IOC_MAGIC, 35, struct rds_raw_data*)
+
+#define FM_IOCTL_SCAN_GETRSSI _IOWR(FM_IOC_MAGIC, 36, struct fm_rssi_req*)
+#define FM_IOCTL_SETMONOSTERO _IOWR(FM_IOC_MAGIC, 37, int32_t)
+#define FM_IOCTL_RDS_BC_RST _IOWR(FM_IOC_MAGIC, 38, int32_t*)
+#define FM_IOCTL_CQI_GET _IOWR(FM_IOC_MAGIC, 39, struct fm_cqi_req*)
+#define FM_IOCTL_GET_HW_INFO _IOWR(FM_IOC_MAGIC, 40, struct fm_hw_info*)
+#define FM_IOCTL_GET_I2S_INFO _IOWR(FM_IOC_MAGIC, 41, fm_i2s_info_t*)
+#define FM_IOCTL_IS_DESE_CHAN _IOWR(FM_IOC_MAGIC, 42, int32_t*)
+#define FM_IOCTL_TOP_RDWR _IOWR(FM_IOC_MAGIC, 43, struct fm_top_rw_parm*)
+#define FM_IOCTL_HOST_RDWR _IOWR(FM_IOC_MAGIC, 44, struct fm_host_rw_parm*)
+
+#define FM_IOCTL_PRE_SEARCH _IOWR(FM_IOC_MAGIC, 45,int32_t)
+#define FM_IOCTL_RESTORE_SEARCH _IOWR(FM_IOC_MAGIC, 46,int32_t)
+
+#define FM_IOCTL_SET_SEARCH_THRESHOLD _IOWR(FM_IOC_MAGIC, 47, fm_search_threshold_t*)
+
+#define FM_IOCTL_GET_AUDIO_INFO _IOWR(FM_IOC_MAGIC, 48, fm_audio_info_t*)
+
+#define FM_IOCTL_SCAN_NEW _IOWR(FM_IOC_MAGIC, 60, struct fm_scan_t*)
+#define FM_IOCTL_SEEK_NEW _IOWR(FM_IOC_MAGIC, 61, struct fm_seek_t*)
+#define FM_IOCTL_TUNE_NEW _IOWR(FM_IOC_MAGIC, 62, struct fm_tune_t*)
+
+#define FM_IOCTL_SOFT_MUTE_TUNE _IOWR(FM_IOC_MAGIC, 63, struct fm_softmute_tune_t*)/*for soft mute tune*/
+#define FM_IOCTL_DESENSE_CHECK _IOWR(FM_IOC_MAGIC, 64, fm_desense_check_t*)
+
+//IOCTL for EM
+#define FM_IOCTL_FULL_CQI_LOG _IOWR(FM_IOC_MAGIC, 70, fm_full_cqi_log_t *)
+
+#define FM_IOCTL_DUMP_REG _IO(FM_IOC_MAGIC, 0xFF)
+
+// ********** ***********FM IOCTL define end *******************************
+
+enum group_idx {
+ mono = 0,
+ stereo,
+ RSSI_threshold,
+ HCC_Enable,
+ PAMD_threshold,
+ Softmute_Enable,
+ De_emphasis,
+ HL_Side,
+ Demod_BW,
+ Dynamic_Limiter,
+ Softmute_Rate,
+ AFC_Enable,
+ Softmute_Level,
+ Analog_Volume,
+ GROUP_TOTAL_NUMS
+};
+
+enum item_idx {
+ Sblend_OFF = 0,
+ Sblend_ON,
+ ITEM_TOTAL_NUMS
+};
+
+struct fm_ctl_parm {
+ uint8_t err;
+ uint8_t addr;
+ uint16_t val;
+ uint16_t rw_flag; // 0:write, 1:read
+};
+
+struct fm_em_parm {
+ uint16_t group_idx;
+ uint16_t item_idx;
+ uint32_t item_value;
+};
+#endif // __FM_H__
diff --git a/jni/fmr/fmr.h b/jni/fmr/fmr.h
new file mode 100644
index 0000000..b666b8f
--- /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 <signal.h>
+#include <errno.h>
+#include <dlfcn.h>
+
+#include "fm.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..df42f65
--- /dev/null
+++ b/jni/fmr/fmr_err.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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..a906887
--- /dev/null
+++ b/jni/fmr/libfm_jni.cpp
@@ -0,0 +1,438 @@
+/*
+ * 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;
+}
+