blob: e6ab1b1901e015905a1aef665c78073ab6b1f21a [file] [log] [blame]
/*
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 <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <utils/Log.h>
#include "radio-helium-commands.h"
#include "radio-helium.h"
#include "fm_hci.h"
#include <dlfcn.h>
#include <errno.h>
fm_vendor_callbacks_t *jni_cb;
int hci_fm_get_signal_threshold();
int hci_fm_enable_recv_req();
int hci_fm_mute_mode_req(struct hci_fm_mute_mode_req );
struct helium_device *radio;
static int oda_agt;
static int grp_mask;
static int rt_plus_carrier = -1;
static int ert_carrier = -1;
static unsigned char ert_buf[256];
static unsigned char ert_len;
static unsigned char c_byt_pair_index;
static char utf_8_flag;
static char rt_ert_flag;
static char formatting_dir;
static uint32_t ch_det_th_mask_flag;
static uint32_t def_data_rd_mask_flag;
static uint32_t blend_tbl_mask_flag;
static uint32_t station_param_mask_flag;
static uint32_t station_dbg_param_mask_flag;
uint64_t flag;
#define LOG_TAG "radio_helium"
static void radio_hci_req_complete(char result)
{
ALOGD("%s:enetred %s", LOG_TAG, __func__);
}
static void radio_hci_status_complete(int result)
{
ALOGD("%s:enetred %s", LOG_TAG, __func__);
}
static void hci_cc_fm_enable_rsp(char *ev_rsp)
{
struct hci_fm_conf_rsp *rsp;
if (ev_rsp == NULL) {
ALOGE("%s:%s, buffer is null\n", LOG_TAG, __func__);
return;
}
rsp = (struct hci_fm_conf_rsp *)ev_rsp;
jni_cb->thread_evt_cb(0);
radio_hci_req_complete(rsp->status);
jni_cb->enabled_cb();
if (rsp->status == FM_HC_STATUS_SUCCESS)
radio->mode = FM_RECV;
}
static void hci_cc_conf_rsp(char *ev_rsp)
{
struct hci_fm_conf_rsp *rsp;
if (ev_rsp == NULL) {
ALOGE("%s:%s, buffer is null\n", LOG_TAG, __func__);
return;
}
rsp = (struct hci_fm_conf_rsp *)ev_rsp;
radio_hci_req_complete(rsp->status);
if (!rsp->status) {
radio->recv_conf = rsp->recv_conf_rsp;
}
}
static void hci_cc_fm_disable_rsp(char *ev_buff)
{
char status;
int ret;
if (ev_buff == NULL) {
ALOGE("%s:%s, buffer is null\n", LOG_TAG, __func__);
return;
}
ALOGE("%s:enetred %s calling ", LOG_TAG, __func__);
status = (char) *ev_buff;
radio_hci_req_complete(status);
if (radio->mode == FM_TURNING_OFF) {
radio->mode = FM_OFF;
jni_cb->disabled_cb();
jni_cb->thread_evt_cb(1);
//close the userial port and power off the chip
ret = fm_power(FM_RADIO_DISABLE);
ALOGI("fm power off status = %d", ret);
ALOGI("%s:calling fm userial close\n", LOG_TAG );
fm_userial_close();
// fm_power(FM_RADIO_DISABLE);
}
}
static void hci_cc_rsp(char *ev_buff)
{
char status;
if (ev_buff == NULL) {
ALOGE("%s:%s, socket buffer is null\n", LOG_TAG, __func__);
return;
}
status = (char)*ev_buff;
radio_hci_req_complete(status);
}
static void hci_cc_rds_grp_cntrs_rsp(char *ev_buff)
{
char status;
if (ev_buff == NULL) {
ALOGE("%s:%s, buffer is null\n", LOG_TAG, __func__);
return;
}
status = ev_buff[0];
ALOGI("%s:%s, status =%d\n", LOG_TAG, __func__,status);
if (status < 0) {
ALOGE("%s:%s, read rds_grp_cntrs failed status=%d\n", LOG_TAG, __func__,status);
}
jni_cb->rds_grp_cntrs_rsp_cb(&ev_buff[1]);
}
static void hci_cc_rds_grp_cntrs_ext_rsp(char *ev_buff)
{
char status;
int i;
if (ev_buff == NULL) {
ALOGE("%s:%s, buffer is null\n", LOG_TAG, __func__);
return;
}
status = ev_buff[0];
ALOGI("%s:%s, status =%d\n", LOG_TAG, __func__,status);
if (status < 0) {
ALOGE("%s:%s, read rds_grp_cntrs_ext failed status=%d\n", LOG_TAG, __func__,status);
}
jni_cb->rds_grp_cntrs_ext_rsp_cb(&ev_buff[1]);
}
static void hci_cc_riva_peek_rsp(char *ev_buff)
{
char status;
if (ev_buff == NULL) {
ALOGE("%s:%s, buffer is null\n", LOG_TAG, __func__);
return;
}
status = ev_buff[0];
ALOGE("%s:%s, status =%d\n", LOG_TAG, __func__,status);
if (status < 0) {
ALOGE("%s:%s, peek failed=%d\n", LOG_TAG, __func__, status);
}
jni_cb->fm_peek_rsp_cb(&ev_buff[PEEK_DATA_OFSET]);
radio_hci_req_complete(status);
}
static void hci_cc_ssbi_peek_rsp(char *ev_buff)
{
char status;
if (ev_buff == NULL) {
ALOGE("%s:%s, buffer is null\n", LOG_TAG, __func__);
return;
}
status = ev_buff[0];
ALOGE("%s:%s, status =%d\n", LOG_TAG, __func__,status);
if (status < 0) {
ALOGE("%s:%s,ssbi peek failed=%d\n", LOG_TAG, __func__, status);
}
jni_cb->fm_ssbi_peek_rsp_cb(&ev_buff[PEEK_DATA_OFSET]);
radio_hci_req_complete(status);
}
static void hci_cc_agc_rsp(char *ev_buff)
{
char status;
ALOGV("inside hci_cc_agc_rsp");
if (ev_buff == NULL) {
ALOGE("%s:%s, buffer is null\n", LOG_TAG, __func__);
return;
}
status = ev_buff[0];
ALOGV("%s:%s, status =%d\n", LOG_TAG, __func__,status);
if (status != 0) {
ALOGE("%s:%s,agc gain failed=%d\n", LOG_TAG, __func__, status);
} else {
jni_cb->fm_agc_gain_rsp_cb(&ev_buff[1]);
}
radio_hci_req_complete(status);
}
static void hci_cc_get_ch_det_threshold_rsp(char *ev_buff)
{
int status;
int val = 0;
if (ev_buff == NULL) {
ALOGE("%s:%s, buffer is null\n", LOG_TAG, __func__);
return;
}
status = ev_buff[0];
ALOGV("%s:%s, status =%d\n", LOG_TAG, __func__,status);
if (status != 0) {
ALOGE("%s:%s,ssbi peek failed=%d\n", LOG_TAG, __func__, status);
} else {
memcpy(&radio->ch_det_threshold, &ev_buff[1],
sizeof(struct hci_fm_ch_det_threshold));
radio_hci_req_complete(status);
if (test_bit(ch_det_th_mask_flag, CMD_CHDET_SINR_TH))
val = radio->ch_det_threshold.sinr;
else if (test_bit(ch_det_th_mask_flag, CMD_CHDET_SINR_SAMPLE))
val = radio->ch_det_threshold.sinr_samples;
else if (test_bit(ch_det_th_mask_flag, CMD_CHDET_INTF_TH_LOW))
val = radio->ch_det_threshold.low_th;
else if (test_bit(ch_det_th_mask_flag, CMD_CHDET_INTF_TH_HIGH))
val = radio->ch_det_threshold.high_th;
}
clear_all_bit(ch_det_th_mask_flag);
jni_cb->fm_get_ch_det_thr_cb(val, status);
}
static void hci_cc_set_ch_det_threshold_rsp(char *ev_buff)
{
int status = ev_buff[0];
jni_cb->fm_set_ch_det_thr_cb(status);
}
static void hci_cc_sig_threshold_rsp(char *ev_buff)
{
int status, val = -1;
ALOGD("hci_cc_sig_threshold_rsp");
status = ev_buff[0];
if (status != 0) {
ALOGE("%s: status= 0x%x", __func__, status);
} else {
val = ev_buff[1];
}
jni_cb->fm_get_sig_thres_cb(val, status);
}
static void hci_cc_default_data_read_rsp(char *ev_buff)
{
int status, val= 0, data_len = 0;
if (ev_buff == NULL) {
ALOGE("Response buffer is null");
return;
}
status = ev_buff[0];
if (status == 0) {
data_len = ev_buff[1];
ALOGV("hci_cc_default_data_read_rsp:data_len = %d", data_len);
memcpy(&radio->def_data, &ev_buff[1], data_len + sizeof(char));
if (test_bit(def_data_rd_mask_flag, CMD_DEFRD_AF_RMSSI_TH)) {
val = radio->def_data.data[AF_RMSSI_TH_OFFSET];
} else if (test_bit(def_data_rd_mask_flag, CMD_DEFRD_AF_RMSSI_SAMPLE)) {
val = radio->def_data.data[AF_RMSSI_SAMPLES_OFFSET];
} else if (test_bit(def_data_rd_mask_flag, CMD_DEFRD_GD_CH_RMSSI_TH)) {
val = radio->def_data.data[GD_CH_RMSSI_TH_OFFSET];
if (val > MAX_GD_CH_RMSSI_TH)
val -= 256;
} else if (test_bit(def_data_rd_mask_flag, CMD_DEFRD_SEARCH_ALGO)) {
val = radio->def_data.data[SRCH_ALGO_TYPE_OFFSET];
} else if (test_bit(def_data_rd_mask_flag, CMD_DEFRD_SINR_FIRST_STAGE)) {
val = radio->def_data.data[SINRFIRSTSTAGE_OFFSET];
if (val > MAX_SINR_FIRSTSTAGE)
val -= 256;
} else if (test_bit(def_data_rd_mask_flag, CMD_DEFRD_RMSSI_FIRST_STAGE)) {
val = radio->def_data.data[RMSSIFIRSTSTAGE_OFFSET];
} else if (test_bit(def_data_rd_mask_flag, CMD_DEFRD_CF0TH12)) {
val = (radio->def_data.data[CF0TH12_BYTE1_OFFSET] |
(radio->def_data.data[CF0TH12_BYTE2_OFFSET] << 8));
} else if (test_bit(def_data_rd_mask_flag, CMD_DEFRD_TUNE_POWER)) {
} else if (test_bit(def_data_rd_mask_flag, CMD_DEFRD_REPEATCOUNT)) {
val = radio->def_data.data[RX_REPEATE_BYTE_OFFSET];
}
} else {
ALOGE("%s: Error: Status= 0x%x", __func__, status);
}
clear_all_bit(def_data_rd_mask_flag);
jni_cb->fm_def_data_read_cb(val, status);
}
static void hci_cc_default_data_write_rsp(char *ev_buff)
{
int status = ev_buff[0];
jni_cb->fm_def_data_write_cb(status);
}
static void hci_cc_get_blend_tbl_rsp(char *ev_buff)
{
int status, val;
if (ev_buff == NULL) {
ALOGE("%s:response buffer in null", LOG_TAG);
return;
}
status = ev_buff[0];
if (status != 0) {
ALOGE("%s: status = 0x%x", LOG_TAG, status);
} else {
memcpy(&radio->blend_tbl, &ev_buff[1],
sizeof(struct hci_fm_blend_table));
ALOGE("hci_cc_get_blend_tbl_rsp: data");
int i;
for (i = 0; i < 8; i++)
ALOGE("data[%d] = 0x%x", i, ev_buff[1 + i]);
if (test_bit(blend_tbl_mask_flag, CMD_BLENDTBL_SINR_HI)) {
val = radio->blend_tbl.BlendSinrHi;
} else if (test_bit(blend_tbl_mask_flag, CMD_BLENDTBL_RMSSI_HI)) {
val = radio->blend_tbl.BlendRmssiHi;
}
}
clear_all_bit(blend_tbl_mask_flag);
jni_cb->fm_get_blend_cb(val, status);
}
static void hci_cc_set_blend_tbl_rsp(char *ev_buff)
{
int status = ev_buff[0];
jni_cb->fm_set_blend_cb(status);
}
static void hci_cc_station_rsp(char *ev_buff)
{
int val, status = ev_buff[0];
if (status == FM_HC_STATUS_SUCCESS) {
memcpy(&radio->fm_st_rsp.station_rsp.station_freq, &ev_buff[1],
sizeof(struct hci_fm_station_rsp) - sizeof(char));
if (test_bit(station_param_mask_flag, CMD_STNPARAM_RSSI)) {
val = radio->fm_st_rsp.station_rsp.rssi;
} else if (test_bit(station_param_mask_flag, CMD_STNPARAM_SINR)) {
val = radio->fm_st_rsp.station_rsp.sinr;
}
}
ALOGE("hci_cc_station_rsp: val =%x, status = %x", val, status);
jni_cb->fm_get_station_param_cb(val, status);
clear_all_bit(station_param_mask_flag);
}
static void hci_cc_dbg_param_rsp(char *ev_buff)
{
int val, status = ev_buff[0];
if (status == FM_HC_STATUS_SUCCESS) {
memcpy(&radio->st_dbg_param, &ev_buff[1],
sizeof(struct hci_fm_dbg_param_rsp));
if (test_bit(station_dbg_param_mask_flag, CMD_STNDBGPARAM_INFDETOUT)) {
val = radio->st_dbg_param.in_det_out;
} else if (test_bit(station_dbg_param_mask_flag, CMD_STNDBGPARAM_IOVERC)) {
val = radio->st_dbg_param.io_verc;
}
}
ALOGE("hci_cc_dbg_param_rsp: val =%x, status = %x", val, status);
jni_cb->fm_get_station_debug_param_cb(val, status);
clear_all_bit(station_dbg_param_mask_flag);
}
static inline void hci_cmd_complete_event(char *buff)
{
uint16_t opcode;
uint8_t *pbuf;
if (buff == NULL) {
ALOGE("%s:%s, buffer is null\n", LOG_TAG, __func__);
return;
}
ALOGV("%s:buff[1] = 0x%x buff[2] = 0x%x", LOG_TAG, buff[1], buff[2]);
opcode = ((buff[2] << 8) | buff[1]);
ALOGV("%s: Received HCI CMD COMPLETE EVENT for the opcode: 0x%x", __func__, opcode);
pbuf = &buff[3];
switch (opcode) {
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_ENABLE_RECV_REQ):
ALOGE("%s: Recvd. CC event for FM_ENABLE_RECV_REQ", __func__);
hci_cc_fm_enable_rsp(pbuf);
break;
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_RECV_CONF_REQ):
hci_cc_conf_rsp(pbuf);
break;
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_DISABLE_RECV_REQ):
hci_cc_fm_disable_rsp(pbuf);
break;
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_RECV_CONF_REQ):
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_MUTE_MODE_REQ):
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_STEREO_MODE_REQ):
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_ANTENNA):
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_SIGNAL_THRESHOLD):
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_CANCEL_SEARCH):
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_GRP):
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_GRP_PROCESS):
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_EN_WAN_AVD_CTRL):
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_EN_NOTCH_CTRL):
hci_cc_rsp(pbuf);
break;
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_CH_DET_THRESHOLD):
hci_cc_set_ch_det_threshold_rsp(pbuf);
break;
case hci_common_cmd_op_pack(HCI_OCF_FM_RESET):
case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_SSBI_POKE_REG):
case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_POKE_DATA):
case hci_diagnostic_cmd_op_pack(HCI_FM_SET_INTERNAL_TONE_GENRATOR):
case hci_common_cmd_op_pack(HCI_OCF_FM_SET_CALIBRATION):
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_EVENT_MASK):
case hci_common_cmd_op_pack(HCI_OCF_FM_SET_SPUR_TABLE):
hci_cc_rsp(pbuf);
break;
case hci_status_param_op_pack(HCI_OCF_FM_READ_GRP_COUNTERS):
hci_cc_rds_grp_cntrs_rsp(pbuf);
break;
case hci_status_param_op_pack(HCI_OCF_FM_READ_GRP_COUNTERS_EXT):
hci_cc_rds_grp_cntrs_ext_rsp(pbuf);
break;
case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_PEEK_DATA):
hci_cc_riva_peek_rsp(buff);
break;
case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_SSBI_PEEK_REG):
hci_cc_ssbi_peek_rsp(buff);
break;
case hci_diagnostic_cmd_op_pack(HCI_FM_SET_GET_RESET_AGC):
hci_cc_agc_rsp(pbuf);
break;
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_CH_DET_THRESHOLD):
hci_cc_get_ch_det_threshold_rsp(pbuf);
break;
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_SIGNAL_THRESHOLD):
hci_cc_sig_threshold_rsp(pbuf);
break;
case hci_common_cmd_op_pack(HCI_OCF_FM_DEFAULT_DATA_READ):
hci_cc_default_data_read_rsp(pbuf);
break;
case hci_common_cmd_op_pack(HCI_OCF_FM_DEFAULT_DATA_WRITE):
hci_cc_default_data_write_rsp(pbuf);
break;
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_BLND_TBL):
hci_cc_get_blend_tbl_rsp(pbuf);
break;
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_BLND_TBL):
hci_cc_set_blend_tbl_rsp(pbuf);
break;
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_STATION_PARAM_REQ):
hci_cc_station_rsp(pbuf);
break;
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_LOW_PASS_FILTER_CTRL):
ALOGI("%s: recived LPF enable event", __func__);
hci_cc_rsp(pbuf);
break;
case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_STATION_DBG_PARAM):
hci_cc_dbg_param_rsp(pbuf);
break;
/* case hci_common_cmd_op_pack(HCI_OCF_FM_GET_SPUR_TABLE):
hci_cc_get_spur_tbl(buff);
break;
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_PROGRAM_SERVICE_REQ):
hci_cc_prg_srv_rsp(buff);
break;
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_RADIO_TEXT_REQ):
hci_cc_rd_txt_rsp(buff);
break;
case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_AF_LIST_REQ):
hci_cc_af_list_rsp(buff);
break;
case hci_common_cmd_op_pack(HCI_OCF_FM_GET_FEATURE_LIST):
hci_cc_feature_list_rsp(buff);
break;
case hci_status_param_op_pack(HCI_OCF_FM_READ_GRP_COUNTERS):
hci_cc_rds_grp_cntrs_rsp(buff);
break;
case hci_common_cmd_op_pack(HCI_OCF_FM_DO_CALIBRATION):
hci_cc_do_calibration_rsp(buff);
break;
default:
ALOGE("opcode 0x%x", opcode);
break; */
}
}
static inline void hci_cmd_status_event(char *st_rsp)
{
struct hci_ev_cmd_status *ev = (void *) st_rsp;
uint16_t opcode;
if (st_rsp == NULL) {
ALOGE("%s:%s, buffer is null\n", LOG_TAG, __func__);
return;
}
ALOGE("%s:st_rsp[2] = 0x%x st_rsp[3] = 0x%x", LOG_TAG, st_rsp[2], st_rsp[3]);
opcode = ((st_rsp[3] << 8) | st_rsp[2]);
ALOGE("%s: Received HCI CMD STATUS EVENT for opcode: 0x%x", __func__, opcode);
radio_hci_status_complete(ev->status);
}
static inline void hci_ev_tune_status(char *buff)
{
memcpy(&radio->fm_st_rsp.station_rsp, &buff[0],
sizeof(struct hci_ev_tune_status));
jni_cb->tune_cb(radio->fm_st_rsp.station_rsp.station_freq);
// if (radio->fm_st_rsp.station_rsp.serv_avble)
// todo callback for threshould
if (radio->fm_st_rsp.station_rsp.stereo_prg)
jni_cb->stereo_status_cb(true);
else if (radio->fm_st_rsp.station_rsp.stereo_prg == 0)
jni_cb->stereo_status_cb(false);
if (radio->fm_st_rsp.station_rsp.rds_sync_status)
jni_cb->rds_avail_status_cb(true);
else
jni_cb->rds_avail_status_cb(false);
}
static inline void hci_ev_search_next(char *buff)
{
jni_cb->scan_next_cb();
}
static inline void hci_ev_stereo_status(char *buff)
{
char st_status;
if (buff == NULL) {
ALOGE("%s:%s, socket buffer is null\n", LOG_TAG,__func__);
return;
}
st_status = buff[0];
if (st_status)
jni_cb->stereo_status_cb(true);
else
jni_cb->stereo_status_cb(false);
}
static void hci_ev_rds_lock_status(char *buff)
{
char rds_status;
if (buff == NULL) {
ALOGE("%s:%s, socket buffer is null\n", LOG_TAG, __func__);
return;
}
rds_status = buff[0];
if (rds_status)
jni_cb->rds_avail_status_cb(true);
else
jni_cb->rds_avail_status_cb(false);
}
static inline void hci_ev_program_service(char *buff)
{
int len;
char *data;
len = (buff[RDS_PS_LENGTH_OFFSET] * RDS_STRING) + RDS_OFFSET;
data = malloc(len);
if (!data) {
ALOGE("%s:Failed to allocate memory", LOG_TAG);
return;
}
data[0] = buff[RDS_PS_LENGTH_OFFSET];
data[1] = buff[RDS_PTYPE];
data[2] = buff[RDS_PID_LOWER];
data[3] = buff[RDS_PID_HIGHER];
data[4] = 0;
memcpy(data+RDS_OFFSET, &buff[RDS_PS_DATA_OFFSET], len-RDS_OFFSET);
ALOGE("SSK call ps-callback");
jni_cb->ps_update_cb(data);
free(data);
}
static inline void hci_ev_radio_text(char *buff)
{
int len = 0;
char *data;
if (buff == NULL) {
ALOGE("%s:%s, buffer is null\n", LOG_TAG,__func__);
return;
}
while ((buff[len+RDS_OFFSET] != 0x0d) && (len < MAX_RT_LENGTH))
len++;
ALOGV("%s:%s: radio text length=%d\n", LOG_TAG, __func__,len);
data = malloc(len+RDS_OFFSET);
if (!data) {
ALOGE("%s:Failed to allocate memory", LOG_TAG);
return;
}
data[0] = len;
data[1] = buff[RDS_PTYPE];
data[2] = buff[RDS_PID_LOWER];
data[3] = buff[RDS_PID_HIGHER];
data[4] = buff[RT_A_B_FLAG_OFFSET];
memcpy(data+RDS_OFFSET, &buff[RDS_OFFSET], len);
data[len+RDS_OFFSET] = 0x00;
jni_cb->rt_update_cb(data);
free(data);
}
static void hci_ev_af_list(char *buff)
{
struct hci_ev_af_list ev;
if (buff == NULL) {
ALOGE("%s:%s, buffer is null\n", LOG_TAG, __func__);
return;
}
ev.tune_freq = *((int *) &buff[0]);
ev.pi_code = *((__le16 *) &buff[PI_CODE_OFFSET]);
ev.af_size = buff[AF_SIZE_OFFSET];
if (ev.af_size > AF_LIST_MAX) {
ALOGE("%s:AF list size received more than available size", LOG_TAG);
return;
}
memcpy(&ev.af_list[0], &buff[AF_LIST_OFFSET],
ev.af_size * sizeof(int));
jni_cb->af_list_update_cb(&ev);
}
static inline void hci_ev_search_compl(char *buff)
{
if (buff == NULL) {
ALOGE("%s:%s,buffer is null\n", LOG_TAG, __func__);
return;
}
radio->search_on = 0;
jni_cb->seek_cmpl_cb(radio->fm_st_rsp.station_rsp.station_freq);
}
static inline void hci_ev_srch_st_list_compl(char *buff)
{
struct hci_ev_srch_list_compl *ev ;
int cnt;
int stn_num;
int rel_freq;
int abs_freq;
int len;
if (buff == NULL) {
ALOGE("%s:%s, buffer is null\n", LOG_TAG,__func__);
return;
}
ev = malloc(sizeof(*ev));
if (!ev) {
ALOGE("%s:Memory allocation failed", LOG_TAG);
return ;
}
ev->num_stations_found = buff[STN_NUM_OFFSET];
len = ev->num_stations_found * PARAMS_PER_STATION + STN_FREQ_OFFSET;
for(cnt = STN_FREQ_OFFSET, stn_num = 0;
(cnt < len) && (stn_num < ev->num_stations_found)
&& (stn_num < SIZE_ARRAY(ev->rel_freq));
cnt += PARAMS_PER_STATION, stn_num++) {
abs_freq = *((int *)&buff[cnt]);
rel_freq = abs_freq - radio->recv_conf.band_low_limit;
rel_freq = (rel_freq * 20) / KHZ_TO_MHZ;
ev->rel_freq[stn_num].rel_freq_lsb = GET_LSB(rel_freq);
ev->rel_freq[stn_num].rel_freq_msb = GET_MSB(rel_freq);
}
len = ev->num_stations_found * 2 + sizeof(ev->num_stations_found);
jni_cb->srch_list_cb((char*)ev);
free(ev);
}
static inline void hci_ev_rt_plus_id(char *buff)
{
char *data = NULL;
int len = 15;
unsigned short int agt;
ALOGD("%s:%s: start", LOG_TAG, __func__);
data = malloc(len);
if (data != NULL) {
data[0] = len;
data[1] = buff[RDS_PTYPE];
data[2] = buff[RDS_PID_LOWER];
data[3] = buff[RDS_PID_HIGHER];
data[4] = buff[3];
memcpy(&data[RDS_OFFSET], &buff[4], len-RDS_OFFSET);
ALOGD("%s:%s: RT+ ID grouptype=0x%x%x\n", LOG_TAG, __func__,data[4]);
free(data);
} else {
ALOGE("%s:memory allocation failed\n", LOG_TAG);
}
}
static void hci_ev_rt_plus_tag(char *buff)
{
char *data = NULL;
int len = 15;
unsigned short int agt;
ALOGD("%s:%s: start", LOG_TAG, __func__);
data = malloc(len);
if (data != NULL) {
data[0] = len;
ALOGI("%s:%s: data length=%d\n", LOG_TAG, __func__,data[0]);
data[1] = buff[RDS_PTYPE];
data[2] = buff[RDS_PID_LOWER];
data[3] = buff[RDS_PID_HIGHER];
data[4] = buff[3];
memcpy(&data[RDS_OFFSET], &buff[4], len-RDS_OFFSET);
// data[len] = 0x00;
jni_cb->rt_plus_update_cb(data);
free(data);
} else {
ALOGE("%s:memory allocation failed\n", LOG_TAG);
}
}
static void hci_ev_ext_country_code(char *buff)
{
char *data = NULL;
int len = ECC_EVENT_BUFSIZE;
ALOGD("%s:%s: start", LOG_TAG, __func__);
data = malloc(len);
if (data != NULL) {
data[0] = len;
ALOGI("%s:%s: data length=%d\n", LOG_TAG, __func__,data[0]);
data[1] = buff[RDS_PTYPE];
data[2] = buff[RDS_PID_LOWER];
data[3] = buff[RDS_PID_HIGHER];
data[4] = buff[3];
memcpy(&data[RDS_OFFSET], &buff[4], len-RDS_OFFSET);
// data[len] = 0x00;
jni_cb->ext_country_code_cb(data);
free(data);
} else {
ALOGE("%s:memory allocation failed\n", LOG_TAG);
}
}
static void hci_ev_ert()
{
char *data = NULL;
if (ert_len <= 0)
return;
data = malloc(ert_len + 3);
if (data != NULL) {
data[0] = ert_len;
data[1] = utf_8_flag;
data[2] = formatting_dir;
memcpy((data + 3), ert_buf, ert_len);
jni_cb->ert_update_cb(data);
free(data);
}
}
static void hci_ev_hw_error(char *buff)
{
ALOGE("%s:%s: start", LOG_TAG, __func__);
jni_cb->disabled_cb();
jni_cb->thread_evt_cb(1);
fm_userial_close();
}
static void hci_buff_ert(struct rds_grp_data *rds_buf)
{
int i;
unsigned short int info_byte = 0;
unsigned short int byte_pair_index;
if (rds_buf == NULL) {
ALOGE("%s:%s, rds buffer is null\n", LOG_TAG, __func__);
return;
}
byte_pair_index = AGT(rds_buf->rdsBlk[1].rdsLsb);
if (byte_pair_index == 0) {
c_byt_pair_index = 0;
ert_len = 0;
}
if (c_byt_pair_index == byte_pair_index) {
c_byt_pair_index++;
for (i = 2; i <= 3; i++) {
info_byte = rds_buf->rdsBlk[i].rdsLsb;
info_byte |= (rds_buf->rdsBlk[i].rdsMsb << 8);
ert_buf[ert_len++] = rds_buf->rdsBlk[i].rdsMsb;
ert_buf[ert_len++] = rds_buf->rdsBlk[i].rdsLsb;
if ((utf_8_flag == 0) && (info_byte == CARRIAGE_RETURN)) {
ert_len -= 2;
break;
} else if ((utf_8_flag == 1) &&
(rds_buf->rdsBlk[i].rdsMsb == CARRIAGE_RETURN)) {
info_byte = CARRIAGE_RETURN;
ert_len -= 2;
break;
} else if ((utf_8_flag == 1) &&
(rds_buf->rdsBlk[i].rdsLsb == CARRIAGE_RETURN)) {
info_byte = CARRIAGE_RETURN;
ert_len--;
break;
}
}
if ((byte_pair_index == MAX_ERT_SEGMENT) ||
(info_byte == CARRIAGE_RETURN)) {
hci_ev_ert();
c_byt_pair_index = 0;
ert_len = 0;
}
} else {
ert_len = 0;
c_byt_pair_index = 0;
}
}
static void hci_ev_raw_rds_group_data(char *buff)
{
unsigned char blocknum, index;
struct rds_grp_data temp;
unsigned int mask_bit;
unsigned short int aid, agt, gtc;
unsigned short int carrier;
index = RDSGRP_DATA_OFFSET;
if (buff == NULL) {
ALOGE("%s:%s, socket buffer is null\n", LOG_TAG, __func__);
return;
}
for (blocknum = 0; blocknum < RDS_BLOCKS_NUM; blocknum++) {
temp.rdsBlk[blocknum].rdsLsb = buff[index];
temp.rdsBlk[blocknum].rdsMsb = buff[index+1];
index = index + 2;
}
aid = AID(temp.rdsBlk[3].rdsLsb, temp.rdsBlk[3].rdsMsb);
gtc = GTC(temp.rdsBlk[1].rdsMsb);
agt = AGT(temp.rdsBlk[1].rdsLsb);
if (gtc == GRP_3A) {
switch (aid) {
case ERT_AID:
/* calculate the grp mask for RDS grp
* which will contain actual eRT text
*
* Bit Pos 0 1 2 3 4 5 6 7
* Grp Type 0A 0B 1A 1B 2A 2B 3A 3B
*
* similary for rest grps
*/
mask_bit = (((agt >> 1) << 1) + (agt & 1));
oda_agt = (1 << mask_bit);
utf_8_flag = (temp.rdsBlk[2].rdsLsb & 1);
formatting_dir = EXTRACT_BIT(temp.rdsBlk[2].rdsLsb,
ERT_FORMAT_DIR_BIT);
if (ert_carrier != agt)
jni_cb->oda_update_cb();
ert_carrier = agt;
break;
case RT_PLUS_AID:
/* calculate the grp mask for RDS grp
* which will contain actual eRT text
*
* Bit Pos 0 1 2 3 4 5 6 7
* Grp Type 0A 0B 1A 1B 2A 2B 3A 3B
*
* similary for rest grps
*/
mask_bit = (((agt >> 1) << 1) + (agt & 1));
oda_agt = (1 << mask_bit);
/*Extract 5th bit of MSB (b7b6b5b4b3b2b1b0)*/
rt_ert_flag = EXTRACT_BIT(temp.rdsBlk[2].rdsMsb,
RT_ERT_FLAG_BIT);
if (rt_plus_carrier != agt)
jni_cb->oda_update_cb();
rt_plus_carrier = agt;
break;
default:
oda_agt = 0;
break;
}
} else {
carrier = gtc;
if ((carrier == rt_plus_carrier)) {
// hci_ev_rt_plus(temp);
}
else if (carrier == ert_carrier) {
ALOGI("%s:: calling event ert", __func__);
hci_buff_ert(&temp);
}
}
}
static void radio_hci_event_packet(char *evt_buf)
{
char evt;
ALOGE("%s:%s: Received %d bytes of HCI EVENT PKT from Controller", LOG_TAG,
__func__, ((FM_EVT_HDR *)evt_buf)->evt_len);
evt = ((FM_EVT_HDR *)evt_buf)->evt_code;
ALOGE("%s:evt: %d", LOG_TAG, evt);
switch(evt) {
case HCI_EV_TUNE_STATUS:
hci_ev_tune_status(((FM_EVT_HDR *)evt_buf)->cmd_params);
break;
case HCI_EV_SEARCH_PROGRESS:
case HCI_EV_SEARCH_RDS_PROGRESS:
case HCI_EV_SEARCH_LIST_PROGRESS:
hci_ev_search_next(((FM_EVT_HDR *)evt_buf)->cmd_params);
break;
case HCI_EV_STEREO_STATUS:
hci_ev_stereo_status(((FM_EVT_HDR *)evt_buf)->cmd_params);
break;
case HCI_EV_RDS_LOCK_STATUS:
hci_ev_rds_lock_status(((FM_EVT_HDR *)evt_buf)->cmd_params);
break;
/* case HCI_EV_SERVICE_AVAILABLE:
hci_ev_service_available(hdev, skb);
break; */
case HCI_EV_RDS_RX_DATA:
hci_ev_raw_rds_group_data(((FM_EVT_HDR *)evt_buf)->cmd_params);
break;
case HCI_EV_PROGRAM_SERVICE:
hci_ev_program_service(((FM_EVT_HDR *)evt_buf)->cmd_params);
break;
case HCI_EV_RADIO_TEXT:
hci_ev_radio_text(((FM_EVT_HDR *)evt_buf)->cmd_params);
break;
case HCI_EV_FM_AF_LIST:
hci_ev_af_list(((FM_EVT_HDR *)evt_buf)->cmd_params);
break;
case HCI_EV_CMD_COMPLETE:
ALOGE("%s:%s: Received HCI_EV_CMD_COMPLETE", LOG_TAG, __func__);
hci_cmd_complete_event(((FM_EVT_HDR *)evt_buf)->cmd_params);
break;
case HCI_EV_CMD_STATUS:
hci_cmd_status_event(((FM_EVT_HDR *)evt_buf)->cmd_params);
break;
case HCI_EV_SEARCH_COMPLETE:
case HCI_EV_SEARCH_RDS_COMPLETE:
hci_ev_search_compl(((FM_EVT_HDR *)evt_buf)->cmd_params);
break;
case HCI_EV_SEARCH_LIST_COMPLETE:
hci_ev_srch_st_list_compl(((FM_EVT_HDR *)evt_buf)->cmd_params);
break;
case HCI_EV_RADIO_TEXT_PLUS_ID:
hci_ev_rt_plus_id(((FM_EVT_HDR *)evt_buf)->cmd_params);
break;
case HCI_EV_RADIO_TEXT_PLUS_TAG:
hci_ev_rt_plus_tag(((FM_EVT_HDR *)evt_buf)->cmd_params);
break;
case HCI_EV_EXT_COUNTRY_CODE:
hci_ev_ext_country_code(((FM_EVT_HDR *)evt_buf)->cmd_params);
break;
case HCI_EV_HW_ERR_EVENT:
hci_ev_hw_error(((FM_EVT_HDR *)evt_buf)->cmd_params);
break;
default:
break;
}
}
/* 'evt_buf' contains the event received from Controller */
int fm_evt_notify(char *evt_buf)
{
ALOGI("%s: %s: Received event notification from FM-HCI thread. EVT CODE: %d ",
LOG_TAG, __func__, ((FM_EVT_HDR *)evt_buf)->evt_code);
radio_hci_event_packet(evt_buf);
return 0;
}
int helium_search_req(int on, int direct)
{
int retval = 0;
enum search_t srch;
int saved_val;
int dir;
srch = radio->g_search_mode & SRCH_MODE;
saved_val = radio->search_on;
radio->search_on = on;
if (direct)
dir = SRCH_DIR_UP;
else
dir = SRCH_DIR_DOWN;
if (on) {
switch (srch) {
case SCAN_FOR_STRONG:
case SCAN_FOR_WEAK:
radio->srch_st_list.srch_list_dir = dir;
radio->srch_st_list.srch_list_mode = srch;
retval = helium_search_list(&radio->srch_st_list);
break;
case RDS_SEEK_PTY:
case RDS_SCAN_PTY:
case RDS_SEEK_PI:
srch = srch - SEARCH_RDS_STNS_MODE_OFFSET;
radio->srch_rds.srch_station.srch_mode = srch;
radio->srch_rds.srch_station.srch_dir = dir;
radio->srch_rds.srch_station.scan_time = radio->g_scan_time;
retval = helium_search_rds_stations(&radio->srch_rds);
break;
default:
radio->srch_st.srch_mode = srch;
radio->srch_st.scan_time = radio->g_scan_time;
radio->srch_st.srch_dir = dir;
retval = helium_search_stations(&radio->srch_st);
break;
}
} else {
retval = helium_cancel_search_req();
}
if (retval < 0)
radio->search_on = saved_val;
return retval;
}
int helium_recv_set_region(int req_region)
{
int retval;
int saved_val;
saved_val = radio->region;
radio->region = req_region;
retval = hci_fm_set_recv_conf_req(&radio->recv_conf);
if (retval < 0)
radio->region = saved_val;
return retval;
}
int set_low_power_mode(int lp_mode)
{
int rds_grps_proc = 0x00;
int retval = 0;
if (radio->power_mode != lp_mode) {
if (lp_mode) {
radio->event_mask = 0x00;
if (radio->af_jump_bit)
rds_grps_proc = 0x00 | AF_JUMP_ENABLE;
else
rds_grps_proc = 0x00;
retval = helium_rds_grp_process_req(rds_grps_proc);
if (retval < 0) {
ALOGE("%s:Disable RDS failed", LOG_TAG);
return retval;
}
retval = helium_set_event_mask_req(radio->event_mask);
} else {
radio->event_mask = SIG_LEVEL_INTR | RDS_SYNC_INTR | AUDIO_CTRL_INTR;
retval = helium_set_event_mask_req(radio->event_mask);
if (retval < 0) {
ALOGE("%s:Enable Async events failed", LOG_TAG);
return retval;
}
radio->g_rds_grp_proc_ps = 0x000000FF;
retval = helium_rds_grp_process_req(radio->g_rds_grp_proc_ps);
}
radio->power_mode = lp_mode;
}
return retval;
}
/* Callback function to be registered with FM-HCI for event notification */
static fm_hal_cb hal_cb = {
fm_evt_notify
};
int hal_init( fm_vendor_callbacks_t *p_cb)
{
int ret = -1;
radio = malloc(sizeof(struct helium_device));
if (!radio) {
ALOGE("%s:Failed to allocate memory for device", LOG_TAG);
return ret;
}
/* Save the JNI callback functions */
jni_cb = p_cb;
/* Initialize the FM-HCI */
ALOGE("%s:%s: Initializing the event notification func with FM-HCI", LOG_TAG, __func__);
ret = fm_hci_init(&hal_cb);
ALOGE("%s:%s: Turning FM ON...", LOG_TAG, __func__);
ret = fm_power(FM_RADIO_ENABLE);
ALOGE("%s:%s: Firmware download and HCI Initialization in-progress...", LOG_TAG, __func__);
/* TODO : Start the preload timer */
open_serial_port();
pthread_mutex_init(&fm_hal, NULL);
return 0;
}
/* Called by the JNI for performing the FM operations */
static int set_fm_ctrl(int cmd, int val)
{
int ret = 0;
int saved_val;
char temp_val = 0;
unsigned int rds_grps_proc = 0;
char *data;
struct hci_fm_def_data_wr_req def_data_wrt;
ALOGE("%s:cmd: %x, val: %d",LOG_TAG, cmd, val);
switch (cmd) {
case HCI_FM_HELIUM_AUDIO_MUTE:
saved_val = radio->mute_mode.hard_mute;
radio->mute_mode.hard_mute = val;
ret = hci_fm_mute_mode_req(radio->mute_mode);
if (ret < 0) {
ALOGE("%s:Error while set FM hard mute %d", LOG_TAG, ret);
radio->mute_mode.hard_mute = saved_val;
}
break;
case HCI_FM_HELIUM_SRCHMODE:
if (is_valid_srch_mode(val))
radio->g_search_mode = val;
else
ret = -EINVAL;
break;
case HCI_FM_HELIUM_SCANDWELL:
if (is_valid_scan_dwell_prd(val))
radio->g_scan_time = val;
else
ret = -EINVAL;
break;
case HCI_FM_HELIUM_SRCHON:
helium_search_req(val, SRCH_DIR_UP);
break;
case HCI_FM_HELIUM_STATE:
switch (val) {
case FM_RECV:
ret = hci_fm_enable_recv_req();
break;
case FM_OFF:
radio->mode = FM_TURNING_OFF;
hci_fm_disable_recv_req();
break;
default:
break;
}
break;
case HCI_FM_HELIUM_REGION:
ret = helium_recv_set_region(val);
break;
case HCI_FM_HELIUM_SIGNAL_TH:
temp_val = val;
ret = helium_set_sig_threshold_req(temp_val);
if (ret < 0) {
ALOGE("%s:Error while setting signal threshold\n", LOG_TAG);
goto end;
}
break;
case HCI_FM_HELIUM_SRCH_PTY:
if (is_valid_pty(val)) {
radio->srch_rds.srch_pty = val;
radio->srch_st_list.srch_pty = val;
} else {
ret = -EINVAL;
}
break;
case HCI_FM_HELIUM_SRCH_PI:
if (is_valid_pi(val))
radio->srch_rds.srch_pi = val;
else
ret = -EINVAL;
break;
case HCI_FM_HELIUM_SRCH_CNT:
if (is_valid_srch_station_cnt(val))
radio->srch_st_list.srch_list_max = val;
else
ret = -EINVAL;
break;
case HCI_FM_HELIUM_SPACING:
saved_val = radio->recv_conf.ch_spacing;
radio->recv_conf.ch_spacing = val;
ret = hci_fm_set_recv_conf_req(&radio->recv_conf);
if (ret < 0) {
ALOGE("%s:Error in setting channel spacing", LOG_TAG);
radio->recv_conf.ch_spacing = saved_val;
goto end;
}
break;
case HCI_FM_HELIUM_EMPHASIS:
saved_val = radio->recv_conf.emphasis;
radio->recv_conf.emphasis = val;
ret = hci_fm_set_recv_conf_req(&radio->recv_conf);
if (ret < 0) {
ALOGE("%s:Error in setting emphasis", LOG_TAG);
radio->recv_conf.emphasis = saved_val;
goto end;
}
break;
case HCI_FM_HELIUM_RDS_STD:
saved_val = radio->recv_conf.rds_std;
radio->recv_conf.rds_std = val;
ret = hci_fm_set_recv_conf_req(&radio->recv_conf);
if (ret < 0) {
ALOGE("%s:Error in rds_std", LOG_TAG);
radio->recv_conf.rds_std = saved_val;
goto end;
}
break;
case HCI_FM_HELIUM_RDSON:
saved_val = radio->recv_conf.rds_std;
radio->recv_conf.rds_std = val;
ret = hci_fm_set_recv_conf_req(&radio->recv_conf);
if (ret < 0) {
ALOGE("%s:Error in rds_std", LOG_TAG);
radio->recv_conf.rds_std = saved_val;
goto end;
}
break;
case HCI_FM_HELIUM_RDSGROUP_MASK:
saved_val = radio->rds_grp.rds_grp_enable_mask;
grp_mask = (grp_mask | oda_agt | val);
radio->rds_grp.rds_grp_enable_mask = grp_mask;
radio->rds_grp.rds_buf_size = 1;
radio->rds_grp.en_rds_change_filter = 0;
ret = helium_rds_grp_mask_req(&radio->rds_grp);
if (ret < 0) {
ALOGE("%s:error in setting group mask\n", LOG_TAG);
radio->rds_grp.rds_grp_enable_mask = saved_val;
goto end;
}
break;
case HCI_FM_HELIUM_RDSGROUP_PROC:
saved_val = radio->g_rds_grp_proc_ps;
rds_grps_proc = radio->g_rds_grp_proc_ps | (val & 0xFF);
radio->g_rds_grp_proc_ps = rds_grps_proc;
ret = helium_rds_grp_process_req(radio->g_rds_grp_proc_ps);
if (ret < 0) {
radio->g_rds_grp_proc_ps = saved_val;
goto end;
}
break;
case HCI_FM_HELIUM_RDS_GRP_COUNTERS:
ALOGD("%s: rds_grp counter read value=%d ", LOG_TAG,val);
ret = hci_fm_get_rds_grpcounters_req(val);
if (ret < 0) {
radio->g_rds_grp_proc_ps = saved_val;
goto end;
}
break;
case HCI_FM_HELIUM_RDS_GRP_COUNTERS_EXT:
ALOGD("%s: rds_grp counter read value=%d ", LOG_TAG,val);
ret = hci_fm_get_rds_grpcounters_ext_req(val);
if (ret < 0) {
radio->g_rds_grp_proc_ps = saved_val;
goto end ;
}
break;
case HCI_FM_HELIUM_SET_NOTCH_FILTER:
ALOGD("%s: set notch filter notch=%d ", LOG_TAG,val);
ret = hci_fm_set_notch_filter_req(val);
if (ret < 0) {
goto end;
}
break;
case HCI_FM_HELIUM_RDSD_BUF:
radio->rds_grp.rds_buf_size = val;
break;
case HCI_FM_HELIUM_PSALL:
saved_val = radio->g_rds_grp_proc_ps;
rds_grps_proc = (val << RDS_CONFIG_OFFSET);
radio->g_rds_grp_proc_ps |= rds_grps_proc;
ret = helium_rds_grp_process_req(radio->g_rds_grp_proc_ps);
if (ret < 0) {
radio->g_rds_grp_proc_ps = saved_val;
goto end;
}
break;
case HCI_FM_HELIUM_AF_JUMP:
saved_val = radio->g_rds_grp_proc_ps;
radio->g_rds_grp_proc_ps &= ~(1 << RDS_AF_JUMP_OFFSET);
radio->af_jump_bit = val;
rds_grps_proc = 0x00;
rds_grps_proc = (val << RDS_AF_JUMP_OFFSET);
radio->g_rds_grp_proc_ps |= rds_grps_proc;
ret = helium_rds_grp_process_req(radio->g_rds_grp_proc_ps);
if (ret < 0) {
radio->g_rds_grp_proc_ps = saved_val;
goto end;
}
break;
case HCI_FM_HELIUM_LP_MODE:
set_low_power_mode(val);
break;
case HCI_FM_HELIUM_ANTENNA:
temp_val = val;
ret = helium_set_antenna_req(temp_val);
if (ret < 0) {
ALOGE("%s:Set Antenna failed retval = %x", LOG_TAG, ret);
goto end;
}
radio->g_antenna = val;
break;
case HCI_FM_HELIUM_SOFT_MUTE:
saved_val = radio->mute_mode.soft_mute;
radio->mute_mode.soft_mute = val;
ret = helium_set_fm_mute_mode_req(&radio->mute_mode);
if (ret < 0) {
ALOGE("%s:Error while setting FM soft mute %d", LOG_TAG, ret);
radio->mute_mode.soft_mute = saved_val;
goto end;
}
break;
case HCI_FM_HELIUM_FREQ:
hci_fm_tune_station_req(val);
break;
case HCI_FM_HELIUM_SEEK:
helium_search_req(1, val);
break;
case HCI_FM_HELIUM_UPPER_BAND:
radio->recv_conf.band_high_limit = val;
break;
case HCI_FM_HELIUM_LOWER_BAND:
radio->recv_conf.band_low_limit = val;
break;
case HCI_FM_HELIUM_AUDIO_MODE:
radio->stereo_mode.stereo_mode = ~val;
hci_set_fm_stereo_mode_req(&radio->stereo_mode);
break;
case HCI_FM_HELIUM_RIVA_ACCS_ADDR:
radio->riva_data_req.cmd_params.start_addr = val;
break;
case HCI_FM_HELIUM_RIVA_ACCS_LEN:
if (is_valid_peek_len(val)) {
radio->riva_data_req.cmd_params.length = val;
} else {
ret = -1;
ALOGE("%s: riva access len is not valid\n", LOG_TAG);
goto end;
}
break;
case HCI_FM_HELIUM_RIVA_PEEK:
radio->riva_data_req.cmd_params.subopcode = RIVA_PEEK_OPCODE;
val = hci_peek_data(&radio->riva_data_req.cmd_params);
break;
case HCI_FM_HELIUM_RIVA_POKE:
if (radio->riva_data_req.cmd_params.length <=
MAX_RIVA_PEEK_RSP_SIZE) {
radio->riva_data_req.cmd_params.subopcode =
RIVA_POKE_OPCODE;
ret = hci_poke_data(&radio->riva_data_req);
} else {
ALOGE("%s: riva access len is not valid for poke\n", LOG_TAG);
ret = -1;
goto end;
}
break;
case HCI_FM_HELIUM_SSBI_ACCS_ADDR:
radio->ssbi_data_accs.start_addr = val;
break;
case HCI_FM_HELIUM_SSBI_POKE:
radio->ssbi_data_accs.data = val;
ret = hci_ssbi_poke_reg(&radio->ssbi_data_accs);
break;
case HCI_FM_HELIUM_SSBI_PEEK:
radio->ssbi_peek_reg.start_address = val;
hci_ssbi_peek_reg(&radio->ssbi_peek_reg);
break;
case HCI_FM_HELIUM_AGC_UCCTRL:
radio->set_get_reset_agc.ucctrl = val;
break;
case HCI_FM_HELIUM_AGC_GAIN_STATE:
radio->set_get_reset_agc.ucgainstate = val;
hci_get_set_reset_agc_req(&radio->set_get_reset_agc);
break;
case HCI_FM_HELIUM_SINR_SAMPLES:
if (!is_valid_sinr_samples(val)) {
ALOGE("%s: sinr samples count is not valid\n", __func__);
ret = -1;
goto end;
}
radio->ch_det_threshold.sinr_samples = val;
ret = set_ch_det_thresholds_req(&radio->ch_det_threshold);
if (ret < 0) {
ALOGE("Failed to set SINR samples %d", ret);
goto end;
}
break;
case HCI_FM_HELIUM_SINR_THRESHOLD:
if (!is_valid_sinr_th(val)) {
ALOGE("%s: sinr threshold is not valid\n", __func__);
ret = -1;
goto end;
}
radio->ch_det_threshold.sinr = val;
ret = set_ch_det_thresholds_req(&radio->ch_det_threshold);
break;
case HCI_FM_HELIUM_INTF_LOW_THRESHOLD:
if (!is_valid_intf_det_low_th(val)) {
ALOGE("%s: intf det low threshold is not valid\n", __func__);
ret = -1;
goto end;
}
radio->ch_det_threshold.low_th = val;
ret = set_ch_det_thresholds_req(&radio->ch_det_threshold);
break;
case HCI_FM_HELIUM_INTF_HIGH_THRESHOLD:
if (!is_valid_intf_det_hgh_th(val)) {
ALOGE("%s: intf high threshold is not valid\n", __func__);
ret = -1;
goto end;
}
radio->ch_det_threshold.high_th = val;
ret = set_ch_det_thresholds_req(&radio->ch_det_threshold);
break;
case HCI_FM_HELIUM_SINRFIRSTSTAGE:
def_data_wrt.mode = FM_SRCH_CONFG_MODE;
def_data_wrt.length = FM_SRCH_CNFG_LEN;
memcpy(&def_data_wrt.data, &radio->def_data.data,
radio->def_data.data_len);
def_data_wrt.data[SINRFIRSTSTAGE_OFFSET] = val;
ret = hci_fm_default_data_write_req(&def_data_wrt);
break;
case HCI_FM_HELIUM_RMSSIFIRSTSTAGE:
def_data_wrt.mode = FM_SRCH_CONFG_MODE;
def_data_wrt.length = FM_SRCH_CNFG_LEN;
memcpy(&def_data_wrt.data, &radio->def_data.data,
radio->def_data.data_len);
def_data_wrt.data[RMSSIFIRSTSTAGE_OFFSET] = val;
ret = hci_fm_default_data_write_req(&def_data_wrt);
break;
case HCI_FM_HELIUM_CF0TH12:
def_data_wrt.mode = FM_SRCH_CONFG_MODE;
def_data_wrt.length = FM_SRCH_CNFG_LEN;
memcpy(&def_data_wrt.data, &radio->def_data.data,
radio->def_data.data_len);
def_data_wrt.data[CF0TH12_BYTE1_OFFSET] = (val & 0xFF);
def_data_wrt.data[CF0TH12_BYTE2_OFFSET] = ((val >> 8) & 0xFF);
ret = hci_fm_default_data_write_req(&def_data_wrt);
break;
case HCI_FM_HELIUM_SRCHALGOTYPE:
def_data_wrt.mode = FM_SRCH_CONFG_MODE;
def_data_wrt.length = FM_SRCH_CNFG_LEN;
memcpy(&def_data_wrt.data, &radio->def_data.data,
radio->def_data.data_len);
def_data_wrt.data[SRCH_ALGO_TYPE_OFFSET] = val;
ret = hci_fm_default_data_write_req(&def_data_wrt);
break;
case HCI_FM_HELIUM_AF_RMSSI_TH:
def_data_wrt.mode = FM_AFJUMP_CONFG_MODE;
def_data_wrt.length = FM_AFJUMP_CNFG_LEN;
memcpy(&def_data_wrt.data, &radio->def_data.data,
radio->def_data.data_len);
def_data_wrt.data[AF_RMSSI_TH_OFFSET] = (val & 0xFF);
ret = hci_fm_default_data_write_req(&def_data_wrt);
break;
case HCI_FM_HELIUM_GOOD_CH_RMSSI_TH:
def_data_wrt.mode = FM_AFJUMP_CONFG_MODE;
def_data_wrt.length = FM_AFJUMP_CNFG_LEN;
memcpy(&def_data_wrt.data, &radio->def_data.data,
radio->def_data.data_len);
def_data_wrt.data[GD_CH_RMSSI_TH_OFFSET] = val;
ret = hci_fm_default_data_write_req(&def_data_wrt);
break;
case HCI_FM_HELIUM_AF_RMSSI_SAMPLES:
def_data_wrt.mode = FM_AFJUMP_CONFG_MODE;
def_data_wrt.length = FM_AFJUMP_CNFG_LEN;
memcpy(&def_data_wrt.data, &radio->def_data.data,
radio->def_data.data_len);
def_data_wrt.data[AF_RMSSI_SAMPLES_OFFSET] = val;
ret = hci_fm_default_data_write_req(&def_data_wrt);
break;
case HCI_FM_HELIUM_RXREPEATCOUNT:
def_data_wrt.mode = RDS_PS0_XFR_MODE;
def_data_wrt.length = RDS_PS0_LEN;
memcpy(&def_data_wrt.data, &radio->def_data.data,
radio->def_data.data_len);
def_data_wrt.data[AF_RMSSI_SAMPLES_OFFSET] = val;
ret = hci_fm_default_data_write_req(&def_data_wrt);
break;
case HCI_FM_HELIUM_BLEND_SINRHI:
if (!is_valid_blend_value(val)) {
ALOGE("%s: sinr samples count is not valid\n", __func__);
ret = -1;
goto end;
}
radio->blend_tbl.BlendSinrHi = val;
ret = hci_fm_set_blend_tbl_req(&radio->blend_tbl);
break;
case HCI_FM_HELIUM_BLEND_RMSSIHI:
if (!is_valid_blend_value(val)) {
ALOGE("%s: sinr samples count is not valid\n", __func__);
ret = -1;
goto end;
}
radio->blend_tbl.BlendRmssiHi = val;
ret = hci_fm_set_blend_tbl_req(&radio->blend_tbl);
break;
case HCI_FM_HELIUM_ENABLE_LPF:
ALOGI("%s: val: %x", __func__, val);
if (!(ret = hci_fm_enable_lpf(val))) {
ALOGI("%s: command sent sucessfully", __func__, val);
}
break;
default:
ALOGE("%s:%s: Not a valid FM CMD!!", LOG_TAG, __func__);
ret = 0;
break;
}
end:
if (ret < 0)
ALOGE("%s:%s: %d cmd failed", LOG_TAG, __func__, cmd);
return ret;
}
static int get_fm_ctrl(int cmd, int val)
{
int ret = 0;
struct hci_fm_def_data_rd_req def_data_rd;
ALOGE("%s: cmd = 0x%x", __func__, cmd);
switch(cmd) {
case HCI_FM_HELIUM_FREQ:
val = radio->fm_st_rsp.station_rsp.station_freq;
break;
case HCI_FM_HELIUM_UPPER_BAND:
val = radio->recv_conf.band_high_limit;
break;
case HCI_FM_HELIUM_LOWER_BAND:
val = radio->recv_conf.band_low_limit;
break;
case HCI_FM_HELIUM_SINR_SAMPLES:
set_bit(ch_det_th_mask_flag, CMD_CHDET_SINR_SAMPLE);
ret = hci_fm_get_ch_det_th();
if (ret != FM_HC_STATUS_SUCCESS)
clear_bit(ch_det_th_mask_flag, CMD_CHDET_SINR_SAMPLE);
break;
case HCI_FM_HELIUM_SINR_THRESHOLD:
set_bit(ch_det_th_mask_flag, CMD_CHDET_SINR_TH);
ret = hci_fm_get_ch_det_th();
if (ret != FM_HC_STATUS_SUCCESS)
clear_bit(ch_det_th_mask_flag, CMD_CHDET_SINR_TH);
break;
case HCI_FM_HELIUM_INTF_LOW_THRESHOLD:
set_bit(ch_det_th_mask_flag, CMD_CHDET_INTF_TH_LOW);
ret = hci_fm_get_ch_det_th();
if (ret != FM_HC_STATUS_SUCCESS)
clear_bit(ch_det_th_mask_flag, CMD_CHDET_INTF_TH_LOW);
break;
case HCI_FM_HELIUM_INTF_HIGH_THRESHOLD:
set_bit(ch_det_th_mask_flag, CMD_CHDET_INTF_TH_HIGH);
ret = hci_fm_get_ch_det_th();
if (ret != FM_HC_STATUS_SUCCESS)
clear_bit(ch_det_th_mask_flag, CMD_CHDET_INTF_TH_HIGH);
break;
case HCI_FM_HELIUM_SINRFIRSTSTAGE:
set_bit(def_data_rd_mask_flag, CMD_DEFRD_SINR_FIRST_STAGE);
def_data_rd.mode = FM_SRCH_CONFG_MODE;
def_data_rd.length = FM_SRCH_CNFG_LEN;
goto cmd;
case HCI_FM_HELIUM_RMSSIFIRSTSTAGE:
set_bit(def_data_rd_mask_flag, CMD_DEFRD_RMSSI_FIRST_STAGE);
def_data_rd.mode = FM_SRCH_CONFG_MODE;
def_data_rd.length = FM_SRCH_CNFG_LEN;
goto cmd;
case HCI_FM_HELIUM_CF0TH12:
set_bit(def_data_rd_mask_flag, CMD_DEFRD_CF0TH12);
def_data_rd.mode = FM_SRCH_CONFG_MODE;
def_data_rd.length = FM_SRCH_CNFG_LEN;
goto cmd;
case HCI_FM_HELIUM_SRCHALGOTYPE:
def_data_rd.mode = FM_SRCH_CONFG_MODE;
def_data_rd.length = FM_SRCH_CNFG_LEN;
set_bit(def_data_rd_mask_flag, CMD_DEFRD_SEARCH_ALGO);
goto cmd;
case HCI_FM_HELIUM_AF_RMSSI_TH:
set_bit(def_data_rd_mask_flag, CMD_DEFRD_AF_RMSSI_TH);
def_data_rd.mode = FM_AFJUMP_CONFG_MODE;
def_data_rd.length = FM_AFJUMP_CNFG_LEN;
goto cmd;
case HCI_FM_HELIUM_GOOD_CH_RMSSI_TH:
set_bit(def_data_rd_mask_flag, CMD_DEFRD_GD_CH_RMSSI_TH);
def_data_rd.mode = FM_AFJUMP_CONFG_MODE;
def_data_rd.length = FM_AFJUMP_CNFG_LEN;
goto cmd;
case HCI_FM_HELIUM_AF_RMSSI_SAMPLES:
set_bit(def_data_rd_mask_flag, CMD_DEFRD_AF_RMSSI_SAMPLE);
def_data_rd.mode = FM_AFJUMP_CONFG_MODE;
def_data_rd.length = FM_AFJUMP_CNFG_LEN;
cmd:
def_data_rd.param_len = 0;
def_data_rd.param = 0;
ret = hci_fm_default_data_read_req(&def_data_rd);
if (ret != FM_HC_STATUS_SUCCESS)
clear_all_bit(def_data_rd_mask_flag);
break;
case HCI_FM_HELIUM_RXREPEATCOUNT:
def_data_rd.mode = RDS_PS0_XFR_MODE;
def_data_rd.length = RDS_PS0_LEN;
def_data_rd.param_len = 0;
def_data_rd.param = 0;
set_bit(def_data_rd_mask_flag, CMD_DEFRD_REPEATCOUNT);
ret = hci_fm_default_data_read_req(&def_data_rd);
if (ret != FM_HC_STATUS_SUCCESS)
clear_bit(def_data_rd_mask_flag, CMD_DEFRD_REPEATCOUNT);
break;
case HCI_FM_HELIUM_BLEND_SINRHI:
set_bit(blend_tbl_mask_flag, CMD_BLENDTBL_SINR_HI);
ret = hci_fm_get_blend_req();
if (ret != FM_HC_STATUS_SUCCESS)
clear_bit(blend_tbl_mask_flag, CMD_BLENDTBL_SINR_HI);
case HCI_FM_HELIUM_BLEND_RMSSIHI:
set_bit(blend_tbl_mask_flag, CMD_BLENDTBL_RMSSI_HI);
ret = hci_fm_get_blend_req();
if (ret != FM_HC_STATUS_SUCCESS)
clear_bit(blend_tbl_mask_flag, CMD_BLENDTBL_RMSSI_HI);
break;
case HCI_FM_HELIUM_IOVERC:
set_bit(station_dbg_param_mask_flag, CMD_STNDBGPARAM_IOVERC);
ret = hci_fm_get_station_dbg_param_req();
if (ret != FM_HC_STATUS_SUCCESS)
clear_bit(station_dbg_param_mask_flag, CMD_STNDBGPARAM_IOVERC);
break;
case HCI_FM_HELIUM_INTDET:
set_bit(station_dbg_param_mask_flag, CMD_STNDBGPARAM_INFDETOUT);
ret = hci_fm_get_station_dbg_param_req();
if (ret != FM_HC_STATUS_SUCCESS)
clear_bit(station_dbg_param_mask_flag, CMD_STNDBGPARAM_INFDETOUT);
break;
case HCI_FM_HELIUM_GET_SINR:
if (radio->mode == FM_RECV) {
set_bit(station_param_mask_flag, CMD_STNPARAM_SINR);
ret = hci_fm_get_station_cmd_param_req();
if (ret != FM_HC_STATUS_SUCCESS)
clear_bit(station_param_mask_flag, CMD_STNPARAM_SINR);
} else {
ALOGE("HCI_FM_HELIUM_GET_SINR: radio is not in recv mode");
ret = -EINVAL;
}
break;
case HCI_FM_HELIUM_RMSSI:
if (radio->mode == FM_RECV) {
set_bit(station_param_mask_flag, CMD_STNPARAM_RSSI);
ret = hci_fm_get_station_cmd_param_req();
if (ret != FM_HC_STATUS_SUCCESS)
clear_bit(station_param_mask_flag, CMD_STNPARAM_RSSI);
} else if (radio->mode == FM_TRANS) {
ALOGE("HCI_FM_HELIUM_RMSSI: radio is not in recv mode");
ret = -EINVAL;
}
break;
default:
break;
}
if (ret < 0)
ALOGE("%s:%s: %d cmd failed", LOG_TAG, __func__, cmd);
return ret;
}
const fm_interface_t FM_HELIUM_LIB_INTERFACE = {
hal_init,
set_fm_ctrl,
get_fm_ctrl
};