[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;
+}
+