libfmjni: Implement hal layer
Implement HAL layer that can be plugged in
with Android FM APP to get fm functionalities.
Change-Id: Id16d37a320fdbacb505ba7ecc431622db17bd4d7
diff --git a/libfm_jni/LibfmJni.cpp b/libfm_jni/LibfmJni.cpp
new file mode 100644
index 0000000..d1a0942
--- /dev/null
+++ b/libfm_jni/LibfmJni.cpp
@@ -0,0 +1,397 @@
+/*
+Copyright (c) 2015, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <jni.h>
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+#include <utils/Log.h>
+#include "FmRadioController.h"
+#include "FM_Const.h"
+
+static FmRadioController * pFMRadio;
+
+jboolean OpenFd(JNIEnv *env, jobject thiz)
+{
+ int ret = 0;
+ pFMRadio = new FmRadioController();
+ if (pFMRadio)
+ ret = pFMRadio->open_dev();
+ else
+ ret = JNI_FALSE;
+ ALOGD("%s, [ret=%d]\n", __func__, ret);
+ return ret? JNI_FALSE: JNI_TRUE;
+}
+
+jboolean CloseFd(JNIEnv *env, jobject thiz)
+{
+ int ret = 0;
+
+ if (pFMRadio)
+ ret = pFMRadio->close_dev();
+ else
+ ret = JNI_FALSE;
+
+ ALOGD("%s, [ret=%d]\n", __func__, ret);
+ return ret? JNI_FALSE: JNI_TRUE;
+}
+
+jboolean TurnOn(JNIEnv *env, jobject thiz, jfloat freq)
+{
+ int ret = 0;
+ int tmp_freq;
+
+ ALOGI("%s, [freq=%d]\n", __func__, (int)freq);
+ tmp_freq = (int)(freq * FREQ_MULT); //Eg, 87.5 * 1000 --> 87500
+ if (!pFMRadio) {
+ pFMRadio = new FmRadioController();
+ }
+ if (pFMRadio)
+ ret = pFMRadio->Pwr_Up(tmp_freq);
+ else
+ ret = JNI_FALSE;
+
+ ALOGD("%s, [ret=%d]\n", __func__, ret);
+ return ret?JNI_FALSE:JNI_TRUE;
+}
+
+jboolean TurnOff(JNIEnv *env, jobject thiz, jint type)
+{
+ int ret = 0;
+
+ if (pFMRadio)
+ ret = pFMRadio->Pwr_Down();
+ else
+ ret = JNI_FALSE;
+
+ ALOGD("%s, [ret=%d]\n", __func__, ret);
+ if (pFMRadio) {
+ delete pFMRadio;
+ pFMRadio = NULL;
+ }
+ return ret?JNI_FALSE:JNI_TRUE;
+}
+
+jboolean SetFreq(JNIEnv *env, jobject thiz, jfloat freq)
+{
+ int ret = 0;
+ int tmp_freq;
+
+ tmp_freq = (int)(freq * FREQ_MULT); //Eg, 87.5 * 10 --> 875
+ if (pFMRadio)
+ ret = pFMRadio->TuneChannel(tmp_freq);
+ else
+ ret = JNI_FALSE;
+
+ ALOGD("%s, [ret=%d]\n", __func__, ret);
+ return ret?JNI_FALSE:JNI_TRUE;
+}
+
+jfloat Seek(JNIEnv *env, jobject thiz, jfloat freq, jboolean isUp)
+{
+ int ret = 0;
+ int tmp_freq;
+ int ret_freq;
+ float val;
+
+ tmp_freq = (int)(freq * FREQ_MULT); //Eg, 87.55 * 100 --> 87550
+ if (pFMRadio)
+ ret = pFMRadio->Set_mute(true);
+ else
+ ret = JNI_FALSE;
+ if (ret) {
+ ALOGE("%s, error, [ret=%d]\n", __func__, ret);
+ }
+ ALOGD("%s, [mute] [ret=%d]\n", __func__, ret);
+ if (pFMRadio)
+ ret = pFMRadio->Seek((int)isUp);
+ else
+ ret = JNI_FALSE;
+ if (ret < 0) {
+ ret_freq = tmp_freq; //seek error, so use original freq
+ }
+
+ ALOGD("%s, [freq=%d] [ret=%d]\n", __func__, ret_freq, ret);
+
+ val = (float)ret/FREQ_MULT; //Eg, 8755 / 100 --> 87.55
+
+ return val;
+}
+
+jshortArray ScanList(JNIEnv *env, jobject thiz)
+{
+ int ret = 0;
+ jshortArray scanList;
+ int chl_cnt = FM_SCAN_CH_SIZE_MAX;
+ uint16_t ScanTBL[FM_SCAN_CH_SIZE_MAX];
+
+ if (pFMRadio)
+ ret = pFMRadio->ScanList(ScanTBL, &chl_cnt);
+ else
+ ret = JNI_FALSE;
+ if (ret < 0) {
+ ALOGE("scan failed!\n");
+ scanList = NULL;
+ goto out;
+ }
+ if (chl_cnt > 0) {
+ scanList = env->NewShortArray(chl_cnt);
+ env->SetShortArrayRegion(scanList, 0, chl_cnt, (const jshort*)&ScanTBL[0]);
+ } else {
+ ALOGE("cnt error, [cnt=%d]\n", chl_cnt);
+ scanList = NULL;
+ }
+
+out:
+ ALOGD("%s, [cnt=%d] [ret=%d]\n", __func__, chl_cnt, ret);
+ return scanList;
+}
+
+jshort GetRdsEvent(JNIEnv *env, jobject thiz)
+{
+ int ret = JNI_FALSE;
+
+ if (pFMRadio)
+ ret = pFMRadio->ReadRDS();
+
+ return ret;
+}
+
+jbyteArray GetPsText(JNIEnv *env, jobject thiz)
+{
+ int ret = 0;
+ jbyteArray PS;
+ char ps[MAX_PS_LEN];
+ int ps_len = 0;
+
+ if (pFMRadio)
+ ret = pFMRadio->Get_ps(ps, &ps_len);
+ else
+ ret = JNI_FALSE;
+ if (ret) {
+ ALOGE("%s, error, [ret=%d]\n", __func__, ret);
+ return NULL;
+ }
+ PS = env->NewByteArray(ps_len);
+ env->SetByteArrayRegion(PS, 0, ps_len, (const jbyte*)ps);
+ ALOGD("%s, [ret=%d]\n", __func__, ret);
+ return PS;
+}
+
+jbyteArray GetRtText(JNIEnv *env, jobject thiz)
+{
+ int ret = 0;
+ jbyteArray RadioText;
+ char rt[MAX_RT_LEN];
+ int rt_len = 0;
+
+ if (pFMRadio)
+ ret = pFMRadio->Get_rt(rt, &rt_len);
+ else
+ ret = JNI_FALSE;
+ if (ret) {
+ ALOGE("%s, error, [ret=%d]\n", __func__, ret);
+ return NULL;
+ }
+ RadioText = env->NewByteArray(rt_len);
+ env->SetByteArrayRegion(RadioText, 0, rt_len, (const jbyte*)rt);
+ ALOGD("%s, [ret=%d]\n", __func__, ret);
+ return RadioText;
+}
+
+jshort GetAfFreq(JNIEnv *env, jobject thiz)
+{
+ int ret = 0;
+ jshort ret_freq = 0;
+
+ if (pFMRadio)
+ ret = pFMRadio->Get_AF_freq((uint16_t*)&ret_freq);
+ else
+ ret = JNI_FALSE;
+ if (ret) {
+ ALOGE("%s, error, [ret=%d]\n", __func__, ret);
+ return 0;
+ }
+ ALOGD("%s, [ret_freq=%d]\n", __func__, ret_freq);
+ return ret_freq;
+}
+
+jint SetRds(JNIEnv *env, jobject thiz, jboolean rdson)
+{
+ int ret = 0;
+
+ if (pFMRadio)
+ ret = pFMRadio->Turn_On_Off_Rds(rdson);
+ else
+ ret = JNI_FALSE;
+ if (ret) {
+ ALOGE("%s, error, [ret=%d]\n", __func__, ret);
+ }
+ ALOGD("%s, [rdson=%d] [ret=%d]\n", __func__, rdson, ret);
+ return ret?JNI_FALSE:JNI_TRUE;
+}
+
+jboolean StopSrch(JNIEnv *env, jobject thiz)
+{
+ int ret = 0;
+
+ if (pFMRadio)
+ ret = pFMRadio->Stop_Scan_Seek();
+ else
+ ret = JNI_FALSE;
+ if (ret) {
+ ALOGE("%s, error, [ret=%d]\n", __func__, ret);
+ }
+ ALOGD("%s, [ret=%d]\n", __func__, ret);
+ return ret?JNI_FALSE:JNI_TRUE;
+}
+
+jint SetMute(JNIEnv *env, jobject thiz, jboolean mute)
+{
+ int ret = 0;
+
+ if (pFMRadio)
+ ret = pFMRadio->Set_mute(mute);
+ else
+ ret = JNI_FALSE;
+ if (ret) {
+ ALOGE("%s, error, [ret=%d]\n", __func__, ret);
+ }
+ ALOGD("%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;
+
+ if (pFMRadio)
+ ret = pFMRadio->IsRds_support();
+ else
+ ret = JNI_FALSE;
+ if (!ret) {
+ ALOGE("%s, error, [ret=%d]\n", __func__, ret);
+ }
+ ALOGD("%s, [ret=%d]\n", __func__, ret);
+ return ret;
+}
+
+/******************************************
+ * SwitchAntenna
+ * Parameter:
+ * antenna:
+ 0 : switch to long antenna
+ 1: switch to short antenna
+ *Return Value:
+ * 0: Success
+ * 1: Failed
+ * 2: Not support
+ ******************************************/
+jint SetAntenna(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 {
+ ALOGE("%s:fail, para error\n", __func__);
+ jret = JNI_FALSE;
+ goto out;
+ }
+ if (pFMRadio)
+ ret = pFMRadio->Antenna_Switch(ana);
+ else
+ ret = JNI_FALSE;
+ if (ret) {
+ ALOGE("switchAntenna(), error\n");
+ jret = 1;
+ } else {
+ jret = 0;
+ }
+out:
+ ALOGD("%s: [antenna=%d] [ret=%d]\n", __func__, ana, ret);
+ return jret;
+}
+
+static const char *classPathNameFM = "com/android/fmradio/FmNative";
+
+static JNINativeMethod gMethods[] = {
+ {"openDev", "()Z", (void*)OpenFd },
+ {"closeDev", "()Z", (void*)CloseFd },
+ {"powerUp", "(F)Z", (void*)TurnOn },
+ {"powerDown", "(I)Z", (void*)TurnOff },
+ {"tune", "(F)Z", (void*)SetFreq },
+ {"seek", "(FZ)F", (void*)Seek },
+ {"autoScan", "()[S", (void*)ScanList },
+ {"stopScan", "()Z", (void*)StopSrch },
+ {"setRds", "(Z)I", (void*)SetRds },
+ {"readRds", "()S", (void*)GetRdsEvent },
+ {"getPs", "()[B", (void*)GetPsText },
+ {"getLrText", "()[B", (void*)GetRtText},
+ {"activeAf", "()S", (void*)GetAfFreq},
+ {"setMute", "(Z)I", (void*)SetMute},
+ {"isRdsSupport", "()I", (void*)IsRdsSupport},
+ {"switchAntenna", "(I)I", (void*)SetAntenna},
+};
+
+int register_android_hardware_fm(JNIEnv* env)
+{
+ return jniRegisterNativeMethods(env, classPathNameFM, gMethods, NELEM(gMethods));
+}
+
+jint JNI_OnLoad(JavaVM *jvm, void *reserved)
+{
+ JNIEnv *e;
+ int status;
+ ALOGE("FM : loading FM-JNI\n");
+
+ if(jvm->GetEnv((void **)&e, JNI_VERSION_1_6)) {
+ ALOGE("JNI version mismatch error");
+ return JNI_ERR;
+ }
+
+ if ((status = register_android_hardware_fm(e)) < 0) {
+ ALOGE("jni adapter service registration failure, status: %d", status);
+ return JNI_ERR;
+ }
+ return JNI_VERSION_1_6;
+}
+