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/FmRadioController.cpp b/libfm_jni/FmRadioController.cpp
new file mode 100644
index 0000000..55bcef4
--- /dev/null
+++ b/libfm_jni/FmRadioController.cpp
@@ -0,0 +1,1434 @@
+/*
+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 <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <fcntl.h>
+#include <utils/Log.h>
+#include <cutils/properties.h>
+#include <sys/ioctl.h>
+#include <math.h>
+#include "FmRadioController.h"
+#include "FmIoctlsInterface.h"
+#include "ConfigFmThs.h"
+#include <linux/videodev2.h>
+
+//Reset all variables to default value
+static FmIoctlsInterface * FmIoct;
+FmRadioController :: FmRadioController
+(
+)
+{
+ cur_fm_state = FM_OFF;
+ prev_freq = -1;
+ seek_scan_canceled = false;
+ af_enabled = 0;
+ rds_enabled = 0;
+ event_listener_canceled = false;
+ is_rds_support = false;
+ is_ps_event_received = false;
+ is_rt_event_received = false;
+ is_af_jump_received = false;
+ mutex_fm_state = PTHREAD_MUTEX_INITIALIZER;
+ mutex_seek_compl_cond = PTHREAD_MUTEX_INITIALIZER;
+ mutex_scan_compl_cond = PTHREAD_MUTEX_INITIALIZER;
+ mutex_tune_compl_cond = PTHREAD_MUTEX_INITIALIZER;
+ mutex_turn_on_cond = PTHREAD_MUTEX_INITIALIZER;
+ turn_on_cond = PTHREAD_COND_INITIALIZER;
+ seek_compl_cond = PTHREAD_COND_INITIALIZER;
+ scan_compl_cond = PTHREAD_COND_INITIALIZER;
+ tune_compl_cond = PTHREAD_COND_INITIALIZER;
+ event_listener_thread = 0;
+ fd_driver = -1;
+ FmIoct = new FmIoctlsInterface();
+}
+
+/* Turn off FM */
+FmRadioController :: ~FmRadioController
+(
+)
+{
+ if((cur_fm_state != FM_OFF)) {
+ Stop_Scan_Seek();
+ set_fm_state(FM_OFF_IN_PROGRESS);
+ FmIoctlsInterface::set_control(fd_driver,
+ V4L2_CID_PRV_STATE, FM_DEV_NONE);
+ }
+ if(event_listener_thread != 0) {
+ event_listener_canceled = true;
+ pthread_join(event_listener_thread, NULL);
+ }
+}
+
+int FmRadioController ::open_dev()
+{
+ int ret = FM_SUCCESS;
+
+ fd_driver = open(FM_DEVICE_PATH, O_RDONLY, O_NONBLOCK);
+
+ if (fd_driver < 0) {
+ ALOGE("%s failed, [fd=%d] %s\n", __func__, fd_driver, FM_DEVICE_PATH);
+ return FM_FAILURE;
+ }
+
+ ALOGD("%s, [fd=%d] \n", __func__, fd_driver);
+ return ret;
+}
+
+int FmRadioController ::close_dev()
+{
+ int ret = 0;
+
+ if (fd_driver > 0) {
+ close(fd_driver);
+ fd_driver = -1;
+ }
+ ALOGD("%s, [fd=%d] [ret=%d]\n", __func__, fd_driver, ret);
+ return ret;
+}
+
+struct timespec FmRadioController :: set_time_out
+(
+ int secs
+)
+{
+ struct timespec ts;
+ struct timeval tp;
+
+ gettimeofday(&tp, NULL);
+ ts.tv_sec = tp.tv_sec;
+ ts.tv_nsec = tp.tv_usec * 1000;
+ ts.tv_sec += secs;
+
+ return ts;
+}
+
+//Get current tuned frequency
+//Return -1 if failed to get freq
+long FmRadioController :: GetChannel
+(
+ void
+)
+{
+ long freq = -1;
+ int ret;
+
+ if((cur_fm_state != FM_OFF) &&
+ (cur_fm_state != FM_ON_IN_PROGRESS)) {
+ ret = FmIoctlsInterface::get_cur_freq(fd_driver, freq);
+ if(ret == FM_SUCCESS) {
+ ALOGI("FM get freq is successfull, freq is: %ld\n", freq);
+ }else {
+ ALOGE("FM get frequency failed, freq is: %ld\n", freq);
+ }
+ }else {
+ ALOGE("FM get freq is not valid in current state\n");
+ }
+ return freq;
+}
+
+int FmRadioController ::Pwr_Up(int freq)
+{
+ int ret = FM_SUCCESS;
+ struct timespec ts;
+ ConfigFmThs thsObj;
+
+ ALOGI("%s,[freq=%d]\n", __func__, freq);
+ if (fd_driver < 0) {
+ ret = open_dev();
+ if (ret != FM_SUCCESS) {
+ ALOGE("Dev open failed\n");
+ return FM_FAILURE;
+ }
+ }
+
+ if (cur_fm_state == FM_OFF) {
+ ALOGE("cur_fm_state = %d\n",cur_fm_state);
+ ret = FmIoctlsInterface::start_fm_patch_dl(fd_driver);
+ if (ret != FM_SUCCESS) {
+ ALOGE("FM patch downloader failed: %d\n", ret);
+ close_dev();
+ set_fm_state(FM_OFF);
+ return FM_FAILURE;
+ }
+ if (event_listener_thread == 0) {
+ ret = pthread_create(&event_listener_thread, NULL,
+ handle_events, this);
+ if (ret == 0) {
+ ALOGI("Lock the mutex for FM turn on cond\n");
+ pthread_mutex_lock(&mutex_turn_on_cond);
+ ts = set_time_out(READY_EVENT_TIMEOUT);
+ ret = FmIoctlsInterface::set_control(fd_driver,
+ V4L2_CID_PRV_STATE, FM_RX);
+ if (ret == FM_SUCCESS) {
+ ALOGI("Waiting for timedout or FM on\n");
+ pthread_cond_timedwait(&turn_on_cond,
+ &mutex_turn_on_cond, &ts);
+ ALOGI("Unlocked mutex & timedout or condition satisfied\n");
+ pthread_mutex_unlock(&mutex_turn_on_cond);
+ if (cur_fm_state == FM_ON) {//after READY event
+ ret = SetBand(BAND_87500_108000);
+ if (ret != FM_SUCCESS) {
+ ALOGE("set band failed\n");
+ ret = FM_FAILURE;
+ goto exit;
+ }
+ ret = SetChannelSpacing(CHAN_SPACE_100);
+ if (ret != FM_SUCCESS) {
+ ALOGE("set channel spacing failed\n");
+ ret = FM_FAILURE;
+ goto exit;
+ }
+ ret = SetDeConstant(DE_EMP50);
+ if (ret != FM_SUCCESS) {
+ ALOGE("set Emphasis failed\n");
+ ret = FM_FAILURE;
+ goto exit;
+ }
+ thsObj.SetRxSearchAfThs(FM_PERFORMANCE_PARAMS, fd_driver);
+ SetStereo();
+ ret = TuneChannel(freq);
+ if (ret != FM_SUCCESS) {
+ ALOGI("FM set freq command failed\n");
+ ret = FM_FAILURE;
+ goto exit;
+ }
+ return FM_SUCCESS;
+ } else { //if time out
+ ret = FM_FAILURE;
+ goto exit;
+ }
+ } else {
+ ALOGE("Set FM on control failed\n");
+ pthread_mutex_unlock(&mutex_turn_on_cond);
+ ALOGI("Unlocked the FM on cond mutex\n");
+ ret = FM_FAILURE;
+ goto close_fd;
+ }
+ } else {
+ ALOGE("FM event listener thread failed: %d\n", ret);
+ set_fm_state(FM_OFF);
+ return FM_FAILURE;
+ }
+ } else {
+ ALOGE("FM event listener threadi existed\n");
+ return FM_SUCCESS;
+ }
+ } else if(cur_fm_state != FM_ON_IN_PROGRESS) {
+ return FM_SUCCESS;
+ } else {
+ return FM_FAILURE;
+ }
+
+exit:
+ FmIoctlsInterface::set_control(fd_driver,
+ V4L2_CID_PRV_STATE, FM_DEV_NONE);
+close_fd:
+ event_listener_canceled = true;
+ pthread_join(event_listener_thread, NULL);
+ close(fd_driver);
+ fd_driver = -1;
+ set_fm_state(FM_OFF);
+
+ ALOGD("%s, [ret=%d]\n", __func__, ret);
+ return ret;
+}
+
+int FmRadioController ::Pwr_Down()
+{
+ int ret = 0;
+
+ if((cur_fm_state != FM_OFF)) {
+ Stop_Scan_Seek();
+ set_fm_state(FM_OFF_IN_PROGRESS);
+ FmIoctlsInterface::set_control(fd_driver,
+ V4L2_CID_PRV_STATE, FM_DEV_NONE);
+ }
+ if(event_listener_thread != 0) {
+ ALOGD("%s, event_listener_thread canceeled\n", __func__);
+ event_listener_canceled = true;
+ pthread_join(event_listener_thread, NULL);
+ }
+ ALOGD("%s, [ret=%d]\n", __func__, ret);
+ return ret;
+}
+//Tune to a Freq
+//Return FM_SUCCESS on success FM_FAILURE
+//on failure
+int FmRadioController :: TuneChannel
+(
+ long freq
+)
+{
+ int ret = FM_SUCCESS;
+ struct timespec ts;
+
+ if((cur_fm_state == FM_ON) &&
+ (freq > 0)) {
+ set_fm_state(FM_TUNE_IN_PROGRESS);
+ ret = FmIoctlsInterface::set_freq(fd_driver,
+ freq);
+ if(ret == FM_SUCCESS) {
+ ALOGI("FM set frequency command set successfully\n");
+ pthread_mutex_lock(&mutex_tune_compl_cond);
+ ts = set_time_out(TUNE_EVENT_TIMEOUT);
+ ret = pthread_cond_timedwait(&tune_compl_cond, &mutex_tune_compl_cond, &ts);
+ pthread_mutex_unlock(&mutex_tune_compl_cond);
+ }else {
+ if((cur_fm_state != FM_OFF)) {
+ set_fm_state(FM_ON);
+ }
+ ALOGE("FM set freq command failed\n");
+ }
+ }else {
+ ALOGE("Fm is not in proper state for tuning to a freq\n");
+ ret = FM_FAILURE;
+ }
+ return ret;
+}
+
+int FmRadioController :: Seek(int dir)
+{
+ int ret = 0;
+ int freq = -1;
+ struct timespec ts;
+
+ if (cur_fm_state != FM_ON) {
+ ALOGE("%s error Fm state: %d\n", __func__,cur_fm_state);
+ return FM_FAILURE;
+ }
+
+ ALOGI("FM seek started\n");
+ set_fm_state(SEEK_IN_PROGRESS);
+ ret = FmIoctlsInterface::set_control(fd_driver,
+ V4L2_CID_PRV_SRCHMODE, SEEK_MODE);
+ if (ret != FM_SUCCESS) {
+ set_fm_state(FM_ON);
+ return FM_FAILURE;
+ }
+
+ ret = FmIoctlsInterface::set_control(fd_driver,
+ V4L2_CID_PRV_SCANDWELL, SEEK_DWELL_TIME);
+ if (ret != FM_SUCCESS) {
+ set_fm_state(FM_ON);
+ return FM_FAILURE;
+ }
+
+ if (dir == 1) {
+ ret = FmIoctlsInterface::start_search(fd_driver,
+ SEARCH_UP);
+ } else {
+ ret = FmIoctlsInterface::start_search(fd_driver,
+ SEARCH_DOWN);
+ }
+
+ if (ret != FM_SUCCESS) {
+ set_fm_state(FM_ON);
+ return FM_FAILURE;
+ }
+ pthread_mutex_lock(&mutex_seek_compl_cond);
+ ts = set_time_out(SEEK_COMPL_TIMEOUT);
+ ret = pthread_cond_timedwait(&seek_compl_cond, &mutex_seek_compl_cond, &ts);
+ pthread_mutex_unlock(&mutex_seek_compl_cond);
+ if ((cur_fm_state != SEEK_IN_PROGRESS) && !seek_scan_canceled) {
+ ALOGI("Seek completed without timeout\n");
+ freq = GetChannel();
+ }
+ seek_scan_canceled = false;
+ return freq;
+}
+
+bool FmRadioController ::IsRds_support
+(
+ void
+)
+{
+ is_rds_support = true;
+ ALOGI("is_rds_support: \n", is_rds_support);
+ return is_rds_support;
+}
+
+//HardMute both audio channels
+int FmRadioController ::MuteOn()
+{
+ int ret;
+
+ ALOGE("cur_fm_state = %d\n", cur_fm_state);
+ if((cur_fm_state != FM_OFF) &&
+ (cur_fm_state != FM_ON_IN_PROGRESS)) {
+ ret = FmIoctlsInterface::set_control(fd_driver,
+ V4L2_CID_AUDIO_MUTE, MUTE_L_R_CHAN);
+ ALOGE("CMD executed mute\n");
+ }else {
+ ret = FM_FAILURE;
+ }
+ return ret;
+}
+
+//Unmute both audio channel
+int FmRadioController ::MuteOff()
+{
+ int ret;
+
+ ALOGE("cur_fm_state = %d\n", cur_fm_state);
+ if((cur_fm_state != FM_OFF) &&
+ (cur_fm_state != FM_ON_IN_PROGRESS)) {
+ ret = FmIoctlsInterface::set_control(fd_driver,
+ V4L2_CID_AUDIO_MUTE, UNMUTE_L_R_CHAN);
+ ALOGE("CMD executed for unmute\n");
+ }else {
+ ret = FM_FAILURE;
+ }
+ return ret;
+}
+
+//
+int FmRadioController ::SetSoftMute(bool mode)
+{
+ int ret;
+
+ if((cur_fm_state != FM_OFF) &&
+ (cur_fm_state != FM_ON_IN_PROGRESS)) {
+ ret = FmIoctlsInterface::set_control(fd_driver,
+ V4L2_CID_PRV_SOFT_MUTE, mode);
+ }else {
+ ret = FM_FAILURE;
+ }
+ return ret;
+}
+
+int FmRadioController :: Set_mute(bool mute)
+{
+ int ret = 0;
+
+ if (mute) {
+ ret = MuteOn();
+ } else {
+ ret = MuteOff();
+ }
+
+ if (ret)
+ ALOGE("%s failed, %d\n", __func__, ret);
+ ALOGD("%s, [mute=%d] [ret=%d]\n", __func__, mute, ret);
+ return ret;
+}
+
+int FmRadioController :: Stop_Scan_Seek
+(
+)
+{
+ int ret;
+
+ if((cur_fm_state == SEEK_IN_PROGRESS) ||
+ (cur_fm_state == SCAN_IN_PROGRESS)) {
+ ret = FmIoctlsInterface::set_control(fd_driver,
+ V4L2_CID_PRV_SRCHON, 0);
+ if (ret == FM_SUCCESS) {
+ ALOGI("FM Seek cancel command set successfully\n");
+ seek_scan_canceled = true;
+ } else {
+ ALOGE("FM Seek cancel command sent failed\n");
+ }
+ } else {
+ ALOGE("FM is not in proper state for cancelling Seek operation\n");
+ ret = FM_FAILURE;
+ }
+ return ret;
+}
+
+int FmRadioController :: ReadRDS() //todo define each RDS flag
+{
+ int ret = 0;
+
+ if (is_ps_event_received)
+ ret |= RDS_EVT_PS_UPDATE;
+ if (is_rt_event_received)
+ ret |= RDS_EVT_RT_UPDATE;
+ if (is_af_jump_received)
+ ret |= RDS_EVT_AF_JUMP;
+
+ return ret;
+}
+
+int FmRadioController :: Get_ps(char *ps, int *ps_len)
+{
+ int ret = 0;
+ int len = 0;
+ char raw_rds[STD_BUF_SIZE];
+
+ ret = FmIoctlsInterface::get_buffer(fd_driver,
+ raw_rds, STD_BUF_SIZE, PS_IND);
+ if (ret <= 0) {
+ return FM_FAILURE;
+ } else {
+ if (raw_rds[PS_STR_NUM_IND] > 0) {
+ if (ps != NULL) {
+ for(int i = 0; i < MAX_PS_LEN; i++) {
+ ps[i] = raw_rds[PS_DATA_OFFSET_IND + i];
+ if (ps[i] == 0) {
+ break;
+ } else if((ps[len] <= LAST_CTRL_CHAR) ||
+ (ps[len] >= FIRST_NON_PRNT_CHAR)) {
+ ps[i] = SPACE_CHAR;
+ continue;
+ }
+ len++;
+ }
+ if (len < (MAX_PS_LEN - 1)) {
+ ps[len] = '\0';
+ *ps_len = len + 1;
+ } else {
+ *ps_len = len;
+ }
+ ALOGI("PS is: %s\n", ps);
+ } else {
+ return FM_FAILURE;
+ }
+ }
+ }
+ is_ps_event_received = false;
+ ALOGD("%s, [ps_len=%d]\n", __func__, *ps_len);
+ return FM_SUCCESS;
+}
+
+int FmRadioController :: Get_rt(char *rt, int *rt_len)
+{
+ int ret = 0;
+ int len = 0;
+ char raw_rds[STD_BUF_SIZE];
+
+ ret = FmIoctlsInterface::get_buffer(fd_driver,
+ raw_rds, STD_BUF_SIZE, RT_IND);
+ if (ret <= 0) {
+ return FM_FAILURE;
+ } else {
+ if (rt != NULL) {
+ if ((raw_rds[RT_LEN_IND] > 0) &&
+ (raw_rds[RT_LEN_IND] <= MAX_RT_LEN)) {
+ for(len = 0; len < raw_rds[RT_LEN_IND]; len++) {
+ rt[len] = raw_rds[RT_DATA_OFFSET_IND + len];
+ ALOGI("Rt byte[%d]: %d\n", len, rt[len]);
+ if ((rt[len] <= LAST_CTRL_CHAR) ||
+ (rt[len] >= FIRST_NON_PRNT_CHAR)) {
+ rt[len] = SPACE_CHAR;
+ continue;
+ }
+ }
+ if (len < (MAX_RT_LEN - 1)) {
+ rt[len] = '\0';
+ *rt_len = len + 1;
+ } else {
+ *rt_len = len;
+ }
+ ALOGI("Rt is: %s\n", rt);
+ ALOGI("RT text A / B: %d\n", raw_rds[RT_A_B_FLAG_IND]);
+ } else {
+ return FM_FAILURE;
+ }
+ } else {
+ return FM_FAILURE;
+ }
+ }
+ is_rt_event_received = false;
+ ALOGD("%s, [rt_len=%d]\n", __func__, *rt_len);
+ return FM_SUCCESS;
+}
+
+int FmRadioController :: Get_AF_freq(uint16_t *ret_freq)
+{
+ int ret =0;
+ ULINT lowBand, highBand;
+ float real_freq = 0;
+
+ ALOGI("get_AF_freq\n");
+ ret = FmIoctlsInterface::get_lowerband_limit(fd_driver,
+ lowBand);
+ if (ret != FM_SUCCESS) {
+ ALOGE("failed to get lowerband: %d\n", ret);
+ return FM_FAILURE;
+ }
+ ALOGI("lowBand = %ld\n",lowBand);
+ ret = FmIoctlsInterface::get_upperband_limit(fd_driver,
+ highBand);
+ if (ret != FM_SUCCESS) {
+ ALOGE("failed to getgherband: %d\n", ret);
+ return FM_FAILURE;
+ }
+ ALOGI("highBand = %ld\n",highBand);
+ real_freq = GetChannel();
+ if ((real_freq < lowBand ) || (real_freq > highBand)) {
+ ALOGE("AF freq is not in band limits\ni");
+ return FM_FAILURE;
+ } else {
+ *ret_freq = real_freq/100;
+ }
+ is_af_jump_received = false;
+ return FM_SUCCESS;
+}
+
+//Emphasis:
+//75microsec: 0, 50 microsec: 1
+//return FM_SUCCESS on success, FM_FAILURE
+//on failure
+int FmRadioController :: SetDeConstant
+(
+ long emphasis
+)
+{
+ int ret;
+
+ ALOGE("cur_fm_state: %d, emphasis: %d\n", cur_fm_state, emphasis);
+ if(cur_fm_state == FM_ON) {
+ switch(emphasis) {
+ case DE_EMP75:
+ case DE_EMP50:
+ ret = FmIoctlsInterface::set_control(fd_driver,
+ V4L2_CID_PRV_EMPHASIS, emphasis);
+ break;
+ default:
+ ALOGE("FM value pass for set Deconstant is invalid\n");
+ ret = FM_FAILURE;
+ break;
+ }
+ }else {
+ ALOGE("FM is not in proper state to set De constant\n");
+ ret = FM_FAILURE;
+ }
+ return ret;
+}
+
+int FmRadioController :: GetStationList
+(
+ uint16_t *scan_tbl, int *max_cnt
+)
+{
+ char srch_list[STD_BUF_SIZE];
+ int ret;
+ ULINT lowBand, highBand;
+ int station_num = 0;
+ int stationList[FM_RX_SRCHLIST_MAX_STATIONS];
+ int tmpFreqByte1=0;
+ int tmpFreqByte2=0;
+ int freq = 0;
+ float real_freq = 0;
+ int i = 0, j = 0;
+
+ ALOGI("getstationList\n");
+ ret = FmIoctlsInterface::get_lowerband_limit(fd_driver,
+ lowBand);
+ if (ret != FM_SUCCESS) {
+ ALOGE("failed to get lowerband: %d\n", ret);
+ return FM_FAILURE;
+ }
+ ALOGI("lowBand = %ld\n",lowBand);
+ ret = FmIoctlsInterface::get_upperband_limit(fd_driver,
+ highBand);
+ if (ret != FM_SUCCESS) {
+ ALOGE("failed to getgherband: %d\n", ret);
+ return FM_FAILURE;
+ }
+ ALOGI("highBand = %ld\n",highBand);
+ ret = FmIoctlsInterface::get_buffer(fd_driver,
+ srch_list, STD_BUF_SIZE, STATION_LIST_IND);
+ if ((int)srch_list[0] >0) {
+ station_num = (int)srch_list[0];
+ }
+ ALOGI("station_num: %d ", station_num);
+ *max_cnt = station_num;
+ for (i=0;i<station_num;i++) {
+ freq = 0;
+ ALOGI(" Byte1 = %d", srch_list[i * NO_OF_BYTES_EACH_FREQ + 1]);
+ ALOGI(" Byte2 = %d", srch_list[i * NO_OF_BYTES_EACH_FREQ + 2]);
+ tmpFreqByte1 = srch_list[i * NO_OF_BYTES_EACH_FREQ + 1] & 0xFF;
+ tmpFreqByte2 = srch_list[i * NO_OF_BYTES_EACH_FREQ + 2] & 0xFF;
+ ALOGI(" tmpFreqByte1 = %d", tmpFreqByte1);
+ ALOGI(" tmpFreqByte2 = %d", tmpFreqByte2);
+ freq = (tmpFreqByte1 & EXTRACT_FIRST_BYTE) << 8;
+ freq |= tmpFreqByte2;
+ ALOGI(" freq: %d", freq);
+ real_freq = (freq * FREQ_MULTIPLEX) + lowBand;
+ ALOGI(" real_freq: %d", real_freq);
+ if ( (real_freq < lowBand ) || (real_freq > highBand) ) {
+ ALOGI("Frequency out of band limits");
+ } else {
+ scan_tbl[j] = (real_freq/SRCH_DIV);
+ ALOGI(" scan_tbl: %d", scan_tbl[j]);
+ j++;
+ }
+ }
+ return FM_SUCCESS;
+}
+
+int FmRadioController ::ScanList
+(
+ uint16_t *scan_tbl, int *max_cnt
+)
+{
+ int ret;
+ struct timespec ts;
+
+ /* Check current state of FM device */
+ if (cur_fm_state == FM_ON) {
+ ALOGI("FM searchlist started\n");
+ set_fm_state(SCAN_IN_PROGRESS);
+ ret = FmIoctlsInterface::set_control(fd_driver,
+ V4L2_CID_PRV_SRCHMODE, SRCHLIST_MODE_STRONG);
+ if (ret != FM_SUCCESS) {
+ set_fm_state(FM_ON);
+ return FM_FAILURE;
+ }
+ ret = FmIoctlsInterface::set_control(fd_driver,
+ V4L2_CID_PRV_SRCH_CNT, FM_RX_SRCHLIST_MAX_STATIONS);
+ if (ret != FM_SUCCESS) {
+ set_fm_state(FM_ON);
+ return FM_FAILURE;
+ }
+ ret = FmIoctlsInterface::start_search(fd_driver,
+ SEARCH_UP);
+ if (ret != FM_SUCCESS) {
+ set_fm_state(FM_ON);
+ return FM_FAILURE;
+ }
+ pthread_mutex_lock(&mutex_scan_compl_cond);
+ ts = set_time_out(SCAN_COMPL_TIMEOUT);
+ ALOGI("Wait for Scan Timeout or scan complete");
+ ret = pthread_cond_timedwait(&scan_compl_cond, &mutex_scan_compl_cond, &ts);
+ ALOGI("Scan complete or timedout");
+ pthread_mutex_unlock(&mutex_scan_compl_cond);
+ if (cur_fm_state == FM_ON && !seek_scan_canceled) {
+ GetStationList(scan_tbl, max_cnt);
+ } else {
+ seek_scan_canceled = false;
+ return FM_FAILURE;
+ }
+ } else {
+ ALOGI("Scanlist: not proper state %d\n",cur_fm_state );
+ return FM_FAILURE;
+ }
+ return FM_SUCCESS;
+}
+
+long FmRadioController :: GetCurrentRSSI
+(
+ void
+)
+{
+ int ret;
+ long rmssi = -129;
+
+ if((cur_fm_state != FM_OFF) &&
+ (cur_fm_state != FM_ON_IN_PROGRESS)) {
+ ret = FmIoctlsInterface::get_rmssi(fd_driver, rmssi);
+ }else {
+ }
+ return rmssi;
+}
+
+//enable, disable value to receive data of a RDS group
+//return FM_SUCCESS on success, FM_FAILURE on failure
+int FmRadioController :: SetRdsGrpProcessing
+(
+ int grps
+)
+{
+ int ret;
+ long mask;
+
+ if(cur_fm_state == FM_ON) {
+ ret = FmIoctlsInterface::get_control(fd_driver,
+ V4L2_CID_PRV_RDSGROUP_PROC, mask);
+ if(ret != FM_SUCCESS) {
+ return ret;
+ }
+ mask &= 0xC7;
+ mask |= ((grps & 0x07) << 3);
+ ret = FmIoctlsInterface::set_control(fd_driver,
+ V4L2_CID_PRV_RDSGROUP_PROC, (int)mask);
+ }else {
+ ret = FM_FAILURE;
+ }
+ return ret;
+}
+
+//Enable RDS data receiving
+//Enable RT, PS, AF Jump, RTPLUS, ERT etc
+int FmRadioController :: EnableRDS
+(
+ void
+)
+{
+ int ret = FM_FAILURE;
+
+ ALOGE("%s:cur_fm_state = %d\n", __func__, cur_fm_state);
+ if (cur_fm_state == FM_ON) {
+ ret = FmIoctlsInterface::set_control(fd_driver,
+ V4L2_CID_PRV_RDSON, 1);
+ if (ret != FM_SUCCESS) {
+ ALOGE("RDS ON failed\n");
+ return ret;
+ }
+ ret = SetRdsGrpProcessing(FM_RX_RDS_GRP_RT_EBL |
+ FM_RX_RDS_GRP_PS_EBL |
+ FM_RX_RDS_GRP_AF_EBL |
+ FM_RX_RDS_GRP_PS_SIMPLE_EBL);
+ if (ret != FM_SUCCESS) {
+ ALOGE("Set RDS grp processing\n");
+ return ret;
+ }
+ ret = FM_SUCCESS;
+ rds_enabled = 1;
+ EnableAF();
+ } else {
+ ALOGE("%s:not in proper state cur_fm_state = %d\n", cur_fm_state);
+ return ret;
+ }
+ return ret;
+}
+
+//Disable all RDS data processing
+//RT, ERT, RT PLUS, PS
+int FmRadioController :: DisableRDS
+(
+ void
+)
+{
+ int ret = FM_FAILURE;
+
+ ALOGE("%s:cur_fm_state = %d\n", __func__, cur_fm_state);
+ if (cur_fm_state == FM_ON) {
+ ret = FmIoctlsInterface::set_control(fd_driver,
+ V4L2_CID_PRV_RDSON, 2);
+ if (ret != FM_SUCCESS) {
+ ALOGE("Disable RDS failed\n");
+ return ret;
+ }
+ ret = FM_SUCCESS;
+ rds_enabled = 0;
+ DisableAF();
+ } else {
+ ALOGE("%s:not in proper state cur_fm_state = %d\n", cur_fm_state);
+ ret = FM_FAILURE;
+ }
+ return ret;
+}
+
+int FmRadioController :: Turn_On_Off_Rds(bool onoff)
+{
+ int ret = 0;
+
+ if (onoff) {
+ ret = EnableRDS();
+ } else {
+ ret = DisableRDS();
+ }
+
+ if (ret) {
+ ALOGE("%s, failed\n", __func__);
+ }
+ ALOGD("%s, [onoff=%d] [ret=%d]\n", __func__, onoff, ret);
+ return ret;
+}
+
+//Enables Alternate Frequency switching
+int FmRadioController :: EnableAF
+(
+ void
+)
+{
+ int ret;
+ long rdsgrps;
+
+ if(cur_fm_state == FM_ON) {
+ ret = FmIoctlsInterface::get_control(fd_driver,
+ V4L2_CID_PRV_RDSGROUP_PROC, rdsgrps);
+ ret = FmIoctlsInterface::set_control(fd_driver,
+ V4L2_CID_PRV_RDSON, 1);
+ if(ret == FM_SUCCESS) {
+ ret = FmIoctlsInterface::set_control(fd_driver,
+ V4L2_CID_PRV_AF_JUMP, 1);
+ if(ret == FM_SUCCESS) {
+ af_enabled = 1;
+ }
+ } else {
+ }
+ } else {
+ ret = FM_FAILURE;
+ }
+ return ret;
+}
+
+//Disables Alternate Frequency switching
+int FmRadioController :: DisableAF
+(
+ void
+)
+{
+ int ret;
+ long rdsgrps;
+
+ if(cur_fm_state == FM_ON) {
+ ret = FmIoctlsInterface::get_control(fd_driver,
+ V4L2_CID_PRV_RDSGROUP_PROC, rdsgrps);
+ if(ret == FM_SUCCESS) {
+ ret = FmIoctlsInterface::set_control(fd_driver,
+ V4L2_CID_PRV_AF_JUMP, 0);
+ if(ret == FM_SUCCESS) {
+ af_enabled = 0;
+ }
+ }else {
+ }
+ }else {
+ ret = FM_FAILURE;
+ }
+ return ret;
+}
+
+//Set regional band
+int FmRadioController :: SetBand
+(
+ long band
+)
+{
+ int ret;
+
+ if(cur_fm_state == FM_ON) {
+ switch(band) {
+ case BAND_87500_108000:
+ ret = FmIoctlsInterface::set_band(fd_driver,
+ 87500, 108000);
+ break;
+ case BAND_76000_108000:
+ ret = FmIoctlsInterface::set_band(fd_driver,
+ 76000, 108000);
+ break;
+ case BAND_76000_90000:
+ ret = FmIoctlsInterface::set_band(fd_driver,
+ 76000, 90000);
+ break;
+ default:
+ ALOGE("Band type: %ld is invalid\n", band);
+ ret = FM_FAILURE;
+ break;
+ }
+ }else {
+ ALOGE("FM is not in proper state to set band type\n");
+ ret = FM_FAILURE;
+ }
+ return ret;
+}
+
+//set spacing for successive channels
+int FmRadioController :: SetChannelSpacing
+(
+ long spacing
+)
+{
+ int ret;
+
+ if (cur_fm_state == FM_ON) {
+ ret = FmIoctlsInterface::set_control(fd_driver,
+ V4L2_CID_PRV_CHAN_SPACING, spacing);
+ } else {
+ ALOGE("FM is not in proper state to set the channel spacing\n");
+ ret = FM_FAILURE;
+ }
+ return ret;
+}
+
+int FmRadioController :: SetStereo
+(
+)
+{
+ int ret;
+
+ if((cur_fm_state != FM_OFF) &&
+ (cur_fm_state != FM_ON_IN_PROGRESS)) {
+ ret = FmIoctlsInterface::set_audio_mode(fd_driver,
+ STEREO);
+ }else {
+ ret = FM_FAILURE;
+ }
+ return ret;
+}
+
+int FmRadioController :: SetMono
+(
+)
+{
+ int ret;
+
+ if((cur_fm_state != FM_OFF) &&
+ (cur_fm_state != FM_ON_IN_PROGRESS)) {
+ ret = FmIoctlsInterface::set_audio_mode(fd_driver,
+ MONO);
+ }else {
+ ret = FM_FAILURE;
+ }
+ return ret;
+}
+
+bool FmRadioController :: GetSoftMute
+(
+)
+{
+ int ret = FM_SUCCESS;
+ long mode = SMUTE_DISABLED;
+
+ if((cur_fm_state != FM_OFF) &&
+ (cur_fm_state != FM_ON_IN_PROGRESS)) {
+ ret = FmIoctlsInterface::get_control(fd_driver,
+ V4L2_CID_PRV_SOFT_MUTE, mode);
+ if(ret == FM_SUCCESS) {
+ ALOGI("FM Get soft mute is successful: %ld\n", mode);
+ }else {
+ ALOGE("FM Get soft mute failed");
+ }
+ }else {
+ ALOGE("FM is not in proper state for getting soft mute\n");
+ ret = FM_FAILURE;
+ }
+ return mode;
+}
+
+int FmRadioController :: Antenna_Switch(int antenna)
+{
+ int ret = 0;
+
+ if (antenna) {
+ ret = FmIoctlsInterface::set_control(fd_driver,
+ V4L2_CID_PRV_ANTENNA, 1);
+ } else {
+ ret = FmIoctlsInterface::set_control(fd_driver,
+ V4L2_CID_PRV_ANTENNA, 0);
+ }
+ ALOGD("%s, antenna type = %d [ret=%d]\n", __func__, antenna, ret);
+ return ret;
+}
+
+int FmRadioController :: get_fm_state
+(
+)
+{
+ return cur_fm_state;
+}
+
+void FmRadioController :: set_fm_state
+(
+ int state
+)
+{
+ pthread_mutex_lock(&mutex_fm_state);
+ cur_fm_state = state;
+ pthread_mutex_unlock(&mutex_fm_state);
+}
+
+void* FmRadioController :: handle_events
+(
+ void *arg
+)
+{
+ int bytesread;
+ char event_buff[STD_BUF_SIZE];
+ bool status = true;
+ FmRadioController *obj_p = static_cast<FmRadioController*>(arg);
+
+ while(status && !obj_p->event_listener_canceled) {
+ bytesread = FmIoctlsInterface::get_buffer(obj_p->fd_driver,
+ event_buff, STD_BUF_SIZE, EVENT_IND);
+ for(int i = 0; i < bytesread; i++) {
+ status = obj_p->process_radio_events(event_buff[i]);
+ if(status == false) {
+ break;
+ }
+ }
+ }
+ return NULL;
+}
+
+int FmRadioController :: SetRdsGrpMask
+(
+ int mask
+)
+{
+ int ret;
+
+ if((cur_fm_state != FM_OFF) &&
+ (cur_fm_state != FM_OFF_IN_PROGRESS) &&
+ (cur_fm_state != FM_ON_IN_PROGRESS)) {
+ ret = FmIoctlsInterface::set_control(fd_driver,
+ V4L2_CID_PRV_RDSGROUP_MASK, mask);
+ }else {
+ ret = FM_FAILURE;
+ }
+ return ret;
+}
+
+void FmRadioController :: handle_enabled_event
+(
+ void
+)
+{
+ ALOGI("FM handle ready Event\n");
+ FmIoctlsInterface::set_control(fd_driver,
+ V4L2_CID_PRV_AUDIO_PATH, AUDIO_DIGITAL_PATH);
+ FmIoctlsInterface::set_calibration(fd_driver);
+ pthread_mutex_lock(&mutex_turn_on_cond);
+ set_fm_state(FM_ON);
+ pthread_cond_broadcast(&turn_on_cond);
+ pthread_mutex_unlock(&mutex_turn_on_cond);
+}
+
+void FmRadioController :: handle_tuned_event
+(
+ void
+)
+{
+ long freq = -1;
+
+ ALOGI("FM handle Tune event\n");
+ freq = GetChannel();
+ switch(cur_fm_state) {
+ case FM_ON:
+ if(af_enabled && (freq != prev_freq)
+ && (prev_freq > 0)) {
+ ALOGI("AF jump happened\n");
+ is_af_jump_received = true;
+ }
+ break;
+ case FM_TUNE_IN_PROGRESS:
+ pthread_mutex_lock(&mutex_tune_compl_cond);
+ set_fm_state(FM_ON);
+ pthread_cond_broadcast(&tune_compl_cond);
+ pthread_mutex_unlock(&mutex_tune_compl_cond);
+ break;
+ case SEEK_IN_PROGRESS:
+ pthread_mutex_lock(&mutex_seek_compl_cond);
+ set_fm_state(FM_ON);
+ pthread_cond_broadcast(&seek_compl_cond);
+ pthread_mutex_unlock(&mutex_seek_compl_cond);
+ break;
+ case SCAN_IN_PROGRESS:
+ break;
+ }
+ prev_freq = freq;
+}
+
+void FmRadioController :: handle_seek_next_event
+(
+ void
+)
+{
+ ALOGI("FM handle seek next event\n");
+}
+
+void FmRadioController :: handle_seek_complete_event
+(
+ void
+)
+{
+ ALOGI("FM handle seek complete event\n");
+}
+
+void FmRadioController :: handle_raw_rds_event
+(
+ void
+)
+{
+
+}
+
+void FmRadioController :: handle_rt_event
+(
+ void
+)
+{
+ ALOGI("FM handle RT event\n");
+ is_rt_event_received = true;
+}
+
+void FmRadioController :: handle_ps_event
+(
+ void
+)
+{
+ ALOGI("FM handle PS event\n");
+ is_ps_event_received = true;
+}
+
+void FmRadioController :: handle_error_event
+(
+ void
+)
+{
+
+}
+
+void FmRadioController :: handle_below_th_event
+(
+ void
+)
+{
+
+}
+
+void FmRadioController :: handle_above_th_event
+(
+ void
+)
+{
+
+}
+
+void FmRadioController :: handle_stereo_event
+(
+ void
+)
+{
+
+}
+void FmRadioController :: handle_mono_event
+(
+ void
+)
+{
+
+}
+
+void FmRadioController :: handle_rds_aval_event
+(
+ void
+)
+{
+ ALOGI("Got rds_aval_event\n");
+ is_rds_support = true;
+}
+
+void FmRadioController :: handle_rds_not_aval_event
+(
+ void
+)
+{
+ ALOGI("Got rds_not_aval_event\n");
+}
+
+void FmRadioController :: handle_srch_list_event
+(
+ void
+)
+{
+ ALOGI("Got srch list event\n");
+ if (cur_fm_state == SCAN_IN_PROGRESS) {
+ pthread_mutex_lock(&mutex_scan_compl_cond);
+ set_fm_state(FM_ON);
+ pthread_cond_broadcast(&scan_compl_cond);
+ pthread_mutex_unlock(&mutex_scan_compl_cond);
+ }
+}
+
+void FmRadioController :: handle_af_list_event
+(
+ void
+)
+{
+ char raw_rds[STD_BUF_SIZE];
+ int ret;
+ int aflist_size;
+ ULINT lower_band;
+ int AfList[MAX_AF_LIST_SIZE];
+
+ ALOGI("Got af list event\n");
+ ret = FmIoctlsInterface::get_buffer(fd_driver,
+ raw_rds, STD_BUF_SIZE, AF_LIST_IND);
+ lower_band = FmIoctlsInterface::get_lowerband_limit(fd_driver,
+ lower_band);
+ ALOGI("raw_rds[0]: %d\n", (raw_rds[0] & 0xff));
+ ALOGI("raw_rds[1]: %d\n", (raw_rds[1] & 0xff));
+ ALOGI("raw_rds[2]: %d\n", (raw_rds[2] & 0xff));
+ ALOGI("raw_rds[3]: %d\n", (raw_rds[3] & 0xff));
+ ALOGI("raw_rds[4]: %d\n", (raw_rds[4] & 0xff));
+ ALOGI("raw_rds[5]: %d\n", (raw_rds[5] & 0xff));
+ ALOGI("raw_rds[6]: %d\n", (raw_rds[6] & 0xff));
+
+ aflist_size = raw_rds[AF_SIZE_IDX] & 0xff;
+ for(int i = 0; i < aflist_size; i++) {
+ AfList[i] = (raw_rds[AF_SIZE_IDX + i * NO_OF_BYTES_AF + 1] & 0xFF) |
+ ((raw_rds[AF_SIZE_IDX + i * NO_OF_BYTES_AF + 2] & 0xFF) << 8) |
+ ((raw_rds[AF_SIZE_IDX + i * NO_OF_BYTES_AF + 3] & 0xFF) << 16) |
+ ((raw_rds[AF_SIZE_IDX + i * NO_OF_BYTES_AF + 4] & 0xFF) << 24);
+ ALOGI("AF: %d\n", AfList[i]);
+ }
+}
+
+void FmRadioController :: handle_disabled_event
+(
+ void
+)
+{
+ //Expected disabled
+ if(cur_fm_state == FM_OFF_IN_PROGRESS) {
+ ALOGI("Expected disabled event\n");
+ }else {//Enexpected disabled
+ ALOGI("Unexpected disabled event\n");
+ }
+
+ set_fm_state(FM_OFF);
+ close(fd_driver);
+ fd_driver = -1;
+
+ //allow tune function to exit
+ pthread_mutex_lock(&mutex_tune_compl_cond);
+ pthread_cond_broadcast(&tune_compl_cond);
+ pthread_mutex_unlock(&mutex_tune_compl_cond);
+ //allow scan function to exit
+ pthread_mutex_lock(&mutex_scan_compl_cond);
+ pthread_cond_broadcast(&scan_compl_cond);
+ pthread_mutex_unlock(&mutex_scan_compl_cond);
+ //Allow seek function to exit
+ pthread_mutex_lock(&mutex_seek_compl_cond);
+ pthread_cond_broadcast(&seek_compl_cond);
+ pthread_mutex_unlock(&mutex_seek_compl_cond);
+}
+
+void FmRadioController :: handle_rds_grp_mask_req_event
+(
+ void
+)
+{
+ SetRdsGrpMask(0);
+}
+
+void FmRadioController :: handle_rt_plus_event
+(
+ void
+)
+{
+ ALOGI("FM handle RT Plus event\n");
+}
+
+void FmRadioController :: handle_af_jmp_event
+(
+ void
+)
+{
+ long freq = -1;
+
+ freq = GetChannel();
+ ALOGI("FM handle AF Jumped event\n");
+ if(af_enabled && (freq != prev_freq)) {
+ ALOGI("AF Jump occured, prevfreq is: %ld, af freq is: %ld\n", prev_freq, freq);
+ }
+ prev_freq = freq;
+}
+
+void FmRadioController :: handle_ert_event
+(
+ void
+)
+{
+ ALOGI("FM handle ERT event\n");
+}
+
+bool FmRadioController :: process_radio_events
+(
+ int event
+)
+{
+ bool ret = true;
+
+ switch(event) {
+ case READY_EVENT:
+ handle_enabled_event();
+ break;
+ case TUNE_EVENT:
+ handle_tuned_event();
+ break;
+ case SEEK_COMPLETE_EVENT:
+ handle_seek_complete_event();
+ break;
+ case SCAN_NEXT_EVENT:
+ handle_seek_next_event();
+ break;
+ case RAW_RDS_EVENT:
+ handle_raw_rds_event();
+ break;
+ case RT_EVENT:
+ handle_rt_event();
+ break;
+ case PS_EVENT:
+ handle_ps_event();
+ break;
+ case ERROR_EVENT:
+ handle_error_event();
+ break;
+ case BELOW_TH_EVENT:
+ handle_below_th_event();
+ break;
+ case ABOVE_TH_EVENT:
+ handle_above_th_event();
+ break;
+ case STEREO_EVENT:
+ handle_stereo_event();
+ break;
+ case MONO_EVENT:
+ handle_mono_event();
+ break;
+ case RDS_AVAL_EVENT:
+ handle_rds_aval_event();
+ break;
+ case RDS_NOT_AVAL_EVENT:
+ handle_rds_not_aval_event();
+ break;
+ case SRCH_LIST_EVENT:
+ handle_srch_list_event();
+ break;
+ case AF_LIST_EVENT:
+ handle_af_list_event();
+ break;
+ case DISABLED_EVENT:
+ handle_disabled_event();
+ ret = false;
+ break;
+ case RDS_GRP_MASK_REQ_EVENT:
+ handle_rds_grp_mask_req_event();
+ break;
+ case RT_PLUS_EVENT:
+ handle_rt_plus_event();
+ break;
+ case ERT_EVENT:
+ handle_ert_event();
+ break;
+ case AF_JMP_EVENT:
+ handle_af_jmp_event();
+ break;
+ default:
+ break;
+ }
+ return ret;
+}