| /* |
| * 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 __unused, jobject thiz __unused) |
| { |
| 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 __unused, jobject thiz __unused) |
| { |
| 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 __unused, jobject thiz __unused, 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 __unused, jobject thiz __unused, 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 __unused, jobject thiz __unused, 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 __unused, jobject thiz __unused, jfloat freq, 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 __unused) |
| { |
| #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 __unused, jobject thiz __unused) |
| { |
| 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 __unused) |
| { |
| 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 __unused) |
| { |
| 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 __unused, jobject thiz __unused) |
| { |
| 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 __unused) |
| { |
| 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 __unused, jobject thiz __unused, 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 __unused, jobject thiz __unused) |
| { |
| 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 __unused, jobject thiz __unused, 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 __unused, jobject thiz __unused) |
| { |
| 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 __unused, jobject thiz __unused, 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 __unused) |
| { |
| 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; |
| } |
| |