From 644b4888d44d3dbdd9861235cb432ebb3b11899c Mon Sep 17 00:00:00 2001 From: Satish kumar sugasi <ssugas@codeaurora.org> Date: Wed, 4 May 2016 20:15:31 -0700 Subject: [PATCH] FM-HAL: Add support for FM Core functionalities FM-HAL Layer contains the core FM Functionality required for carrying out various FM operations such as FM Enable, FM Tune, FM Seek, FM Search, FM Disable etc.. FM-HAL layer interacts with the FM-HCI layer for sending commands and receiving events to and from Controller. Change-Id: I0ac1c9c80671e43aafa30ce2b68f5ee695c9d764 --- Android.mk | 12 +- helium/Android.mk | 29 + helium/radio-helium-commands.h | 99 +++ helium/radio-helium.h | 1137 +++++++++++++++++++++++++++++++ helium/radio_helium_hal.c | 1067 +++++++++++++++++++++++++++++ helium/radio_helium_hal_cmds.c | 256 +++++++ jni/Android.mk | 2 + jni/FmConst.h | 8 + jni/android_hardware_fm.cpp | 504 +++++++++++++- qcom/fmradio/FmReceiver.java | 33 +- qcom/fmradio/FmReceiverJNI.java | 173 ++++- qcom/fmradio/FmTransceiver.java | 6 +- 12 files changed, 3275 insertions(+), 51 deletions(-) create mode 100644 helium/Android.mk create mode 100644 helium/radio-helium-commands.h create mode 100644 helium/radio-helium.h create mode 100644 helium/radio_helium_hal.c create mode 100644 helium/radio_helium_hal_cmds.c diff --git a/Android.mk b/Android.mk index 4777981..5b79fbf 100644 --- a/Android.mk +++ b/Android.mk @@ -21,10 +21,16 @@ LOCAL_PATH := $(LOCAL_DIR_PATH) include $(LOCAL_PATH)/fmapp2/Android.mk #LOCAL_PATH := $(LOCAL_DIR_PATH) #include $(LOCAL_PATH)/FMRecord/Android.mk -#endif # is-vendor-board-platform -#endif # BOARD_HAVE_QCOM_FM -#endif # Not (TARGET_USES_AOSP) +LOCAL_PATH := $(LOCAL_DIR_PATH) +include $(LOCAL_PATH)/fm_hci/Android.mk + +LOCAL_PATH := $(LOCAL_DIR_PATH) +include $(LOCAL_PATH)/helium/Android.mk LOCAL_PATH := $(LOCAL_DIR_PATH) include $(LOCAL_PATH)/libfm_jni/Android.mk +#endif # is-vendor-board-platform +#endif # BOARD_HAVE_QCOM_FM + +#endif # Not (TARGET_USES_AOSP) diff --git a/helium/Android.mk b/helium/Android.mk new file mode 100644 index 0000000..5cbc551 --- /dev/null +++ b/helium/Android.mk @@ -0,0 +1,29 @@ +#ifeq ($(BOARD_HAVE_QCOM_FM),true) +#ifneq (,$(filter $(QCOM_BOARD_PLATFORMS),$(TARGET_BOARD_PLATFORM))) + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + radio_helium_hal.c \ + radio_helium_hal_cmds.c + +LOCAL_SHARED_LIBRARIES := \ + libfm-hci \ + libdl \ + libnativehelper \ + libcutils + +FM_HCI_DIR:= vendor/qcom/opensource/fm + +LOCAL_C_INCLUDES += $(FM_HCI_DIR)/fm_hci + +LOCAL_MODULE := fm_helium +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) + +#endif # is-vendor-board-platform +#endif # BOARD_HAVE_QCOM_FM + + diff --git a/helium/radio-helium-commands.h b/helium/radio-helium-commands.h new file mode 100644 index 0000000..85acf16 --- /dev/null +++ b/helium/radio-helium-commands.h @@ -0,0 +1,99 @@ +/* +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. +*/ + +#ifndef __RADIO_CHEROKEE_COMMANDS_H +#define __RADIO_CHEROKEE_COMMANDS_H + +enum helium_cmd_t { + HCI_FM_HELIUM_SRCHMODE = 0x8000000 + 1, + HCI_FM_HELIUM_SCANDWELL, + HCI_FM_HELIUM_SRCHON, + HCI_FM_HELIUM_STATE, + HCI_FM_HELIUM_TRANSMIT_MODE, + HCI_FM_HELIUM_RDSGROUP_MASK, + HCI_FM_HELIUM_REGION, + HCI_FM_HELIUM_SIGNAL_TH, + HCI_FM_HELIUM_SRCH_PTY, + HCI_FM_HELIUM_SRCH_PI, + HCI_FM_HELIUM_SRCH_CNT, + HCI_FM_HELIUM_EMPHASIS, + HCI_FM_HELIUM_RDS_STD, + HCI_FM_HELIUM_SPACING, + HCI_FM_HELIUM_RDSON, + HCI_FM_HELIUM_RDSGROUP_PROC, + HCI_FM_HELIUM_LP_MODE, + HCI_FM_HELIUM_ANTENNA, + HCI_FM_HELIUM_RDSD_BUF, + HCI_FM_HELIUM_PSALL, + + /*v4l2 Tx controls*/ + HCI_FM_HELIUM_IOVERC, + HCI_FM_HELIUM_INTDET, + HCI_FM_HELIUM_MPX_DCC, + HCI_FM_HELIUM_AF_JUMP, + HCI_FM_HELIUM_RSSI_DELTA, + HCI_FM_HELIUM_HLSI, + + /*Diagnostic commands*/ + HCI_FM_HELIUM_SOFT_MUTE, + HCI_FM_HELIUM_RIVA_ACCS_ADDR, + HCI_FM_HELIUM_RIVA_ACCS_LEN, + HCI_FM_HELIUM_RIVA_PEEK, + HCI_FM_HELIUM_RIVA_POKE, + HCI_FM_HELIUM_SSBI_ACCS_ADDR, + HCI_FM_HELIUM_SSBI_PEEK, + HCI_FM_HELIUM_SSBI_POKE, + HCI_FM_HELIUM_TX_TONE, + HCI_FM_HELIUM_RDS_GRP_COUNTERS, + HCI_FM_HELIUM_SET_NOTCH_FILTER, /* 0x8000028 */ + HCI_FM_HELIUM_SET_AUDIO_PATH, + HCI_FM_HELIUM_DO_CALIBRATION, + HCI_FM_HELIUM_SRCH_ALGORITHM, + HCI_FM_HELIUM_GET_SINR, + HCI_FM_HELIUM_RXREPEATCOUNT, + HCI_FM_HELIUM_RSSI_TH, + HCI_FM_HELIUM_AF_JUMP_RSSI_TH, + HCI_FM_HELIUM_BLEND_SINRHI, + HCI_FM_HELIUM_BLEND_RMSSIHI, + + /*using private CIDs under userclass*/ + HCI_FM_HELIUM_READ_DEFAULT = 0x00980928, + HCI_FM_HELIUM_WRITE_DEFAULT, + HCI_FM_HELIUM_SET_CALIBRATION, + HCI_FM_HELIUM_SET_SPURTABLE = 0x0098092D, + HCI_FM_HELIUM_GET_SPUR_TBL = 0x0098092E, + HCI_FM_HELIUM_FREQ, + HCI_FM_HELIUM_SEEK, + HCI_FM_HELIUM_UPPER_BAND, + HCI_FM_HELIUM_LOWER_BAND, + HCI_FM_HELIUM_AUDIO_MODE, + HCI_FM_HELIUM_AUDIO_MUTE, +}; + +#endif /* __RADIO_CHEROKEE_COMMANDS_H */ diff --git a/helium/radio-helium.h b/helium/radio-helium.h new file mode 100644 index 0000000..cb85bcc --- /dev/null +++ b/helium/radio-helium.h @@ -0,0 +1,1137 @@ +/* +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. +*/ + +#ifndef __UAPI_RADIO_HCI_CORE_H +#define __UAPI_RADIO_HCI_CORE_H + +#pragma pack(1) + +#include <stdbool.h> + +pthread_mutex_t fm_hal; +#define MIN_TX_TONE_VAL 0x00 +#define MAX_TX_TONE_VAL 0x07 +#define MIN_HARD_MUTE_VAL 0x00 +#define MAX_HARD_MUTE_VAL 0x03 +#define MIN_SRCH_MODE 0x00 +#define MAX_SRCH_MODE 0x09 +#define MIN_SCAN_DWELL 0x00 +#define MAX_SCAN_DWELL 0x0F +#define MIN_SIG_TH 0x00 +#define MAX_SIG_TH 0x03 +#define MIN_PTY 0X00 +#define MAX_PTY 0x1F +#define MIN_PI 0x0000 +#define MAX_PI 0xFFFF +#define MIN_SRCH_STATIONS_CNT 0x00 +#define MAX_SRCH_STATIONS_CNT 0x14 +#define MIN_CHAN_SPACING 0x00 +#define MAX_CHAN_SPACING 0x02 +#define MIN_EMPHASIS 0x00 +#define MAX_EMPHASIS 0x01 +#define MIN_RDS_STD 0x00 +#define MAX_RDS_STD 0x02 +#define MIN_ANTENNA_VAL 0x00 +#define MAX_ANTENNA_VAL 0x01 +#define MIN_TX_PS_REPEAT_CNT 0x01 +#define MAX_TX_PS_REPEAT_CNT 0x0F +#define MIN_SOFT_MUTE 0x00 +#define MAX_SOFT_MUTE 0x01 +#define MIN_PEEK_ACCESS_LEN 0x01 +#define MAX_PEEK_ACCESS_LEN 0xF9 +#define MIN_RESET_CNTR 0x00 +#define MAX_RESET_CNTR 0x01 +#define MIN_HLSI 0x00 +#define MAX_HLSI 0x02 +#define MIN_NOTCH_FILTER 0x00 +#define MAX_NOTCH_FILTER 0x02 +#define MIN_INTF_DET_OUT_LW_TH 0x00 +#define MAX_INTF_DET_OUT_LW_TH 0xFF +#define MIN_INTF_DET_OUT_HG_TH 0x00 +#define MAX_INTF_DET_OUT_HG_TH 0xFF +#define MIN_SINR_TH -128 +#define MAX_SINR_TH 127 +#define MIN_SINR_SAMPLES 0x01 +#define MAX_SINR_SAMPLES 0xFF +#define MIN_BLEND_HI -128 +#define MAX_BLEND_HI 127 + + +/* ---- HCI Packet structures ---- */ +#define RADIO_HCI_COMMAND_HDR_SIZE sizeof(struct radio_hci_command_hdr) +#define RADIO_HCI_EVENT_HDR_SIZE sizeof(struct radio_hci_event_hdr) + +/* HCI data types */ +#define RADIO_HCI_COMMAND_PKT 0x11 +#define RADIO_HCI_EVENT_PKT 0x14 +/*HCI reponce packets*/ +#define MAX_RIVA_PEEK_RSP_SIZE 251 +/* default data access */ +#define DEFAULT_DATA_OFFSET 2 +#define DEFAULT_DATA_SIZE 249 +/* Power levels are 0-7, but SOC will expect values from 0-255 + * So the each level step size will be 255/7 = 36 */ +#define FM_TX_PWR_LVL_STEP_SIZE 36 +#define FM_TX_PWR_LVL_0 0 /* Lowest power lvl that can be set for Tx */ +#define FM_TX_PWR_LVL_MAX 7 /* Max power lvl for Tx */ +#define FM_TX_PHY_CFG_MODE 0x3c +#define FM_TX_PHY_CFG_LEN 0x10 +#define FM_TX_PWR_GAIN_OFFSET 14 +/**RDS CONFIG MODE**/ +#define FM_RDS_CNFG_MODE 0x0f +#define FM_RDS_CNFG_LEN 0x10 +#define AF_RMSSI_TH_LSB_OFFSET 10 +#define AF_RMSSI_TH_MSB_OFFSET 11 +#define AF_RMSSI_SAMPLES_OFFSET 15 +/**RX CONFIG MODE**/ +#define FM_RX_CONFG_MODE 0x15 +#define FM_RX_CNFG_LEN 0x20 +#define GD_CH_RMSSI_TH_OFFSET 12 +#define MAX_GD_CH_RMSSI_TH 127 +#define SRCH_ALGO_TYPE_OFFSET 25 +#define SINRFIRSTSTAGE_OFFSET 26 +#define RMSSIFIRSTSTAGE_OFFSET 27 +#define CF0TH12_BYTE1_OFFSET 8 +#define CF0TH12_BYTE2_OFFSET 9 +#define CF0TH12_BYTE3_OFFSET 10 +#define CF0TH12_BYTE4_OFFSET 11 +#define MAX_SINR_FIRSTSTAGE 127 +#define MAX_RMSSI_FIRSTSTAGE 127 +#define RDS_PS0_XFR_MODE 0x01 +#define RDS_PS0_LEN 6 +#define RX_REPEATE_BYTE_OFFSET 5 +#define FM_SPUR_TBL_SIZE 240 +#define SPUR_DATA_LEN 16 +#define ENTRIES_EACH_CMD 15 +#define SPUR_DATA_INDEX 2 +#define FM_AF_LIST_MAX_SIZE 200 +#define AF_LIST_MAX (FM_AF_LIST_MAX_SIZE / 4) /* Each AF frequency consist + of sizeof(int) bytes */ +#define MAX_BLEND_INDEX 49 +/* HCI timeouts */ +#define RADIO_HCI_TIMEOUT (10000) /* 10 seconds */ + +typedef enum { + ASSOCIATE_JVM, + DISASSOCIATE_JVM +} bt_cb_thread_evt; + +#define TUNE_PARAM 16 +#define SIZE_ARRAY(x) (sizeof(x) / sizeof((x)[0])) +typedef void (*enb_result_cb)(); +typedef void (*tune_rsp_cb)(int Freq); +typedef void (*seek_rsp_cb)(int Freq); +typedef void (*scan_rsp_cb)(); +typedef void (*srch_list_rsp_cb)(uint16_t *scan_tbl); +typedef void (*stereo_mode_cb)(bool status); +typedef void (*rds_avl_sts_cb)(bool status); +typedef void (*af_list_cb)(uint16_t *af_list); +typedef void (*rt_cb)(char *rt); +typedef void (*ps_cb)(char *ps); +typedef void (*oda_cb)(); +typedef void (*rt_plus_cb)(char *rt_plus); +typedef void (*ert_cb)(char *ert); +typedef void (*disable_cb)(); +typedef void (*callback_thread_event)(unsigned int evt); + +typedef struct { + size_t size; + + enb_result_cb enabled_cb; + tune_rsp_cb tune_cb; + seek_rsp_cb seek_cmpl_cb; + scan_rsp_cb scan_next_cb; + srch_list_rsp_cb srch_list_cb; + stereo_mode_cb stereo_status_cb; + rds_avl_sts_cb rds_avail_status_cb; + af_list_cb af_list_update_cb; + rt_cb rt_update_cb; + ps_cb ps_update_cb; + oda_cb oda_update_cb; + rt_plus_cb rt_plus_update_cb; + ert_cb ert_update_cb; + disable_cb disabled_cb; + callback_thread_event thread_evt_cb; +} fm_vendor_callbacks_t; + +pthread_mutex_t radio_fm_cmd; +typedef struct { + int (*init)(const fm_vendor_callbacks_t *p_cb); + int (*set_fm_ctrl)(int opcode, int val); + void (*Get_fm_ctrl) (int opcode, int val); +} fm_interface_t; + +typedef int (*fm_evt_notify_cb)(unsigned char *p_buf); + +typedef struct { + fm_evt_notify_cb fm_evt_notify; +} fm_hal_cb; + +struct radio_hci_command_hdr { + short opcode; /* OCF & OGF */ + char plen; +} ; + +struct radio_hci_event_hdr { + char evt; + char plen; +} ; + +struct radio_hci_dev { + char name[8]; + unsigned long flags; + short id; + char bus; + char dev_type; + char dev_name[248]; + char dev_class[3]; + char features[8]; + char commands[64]; + unsigned int data_block_len; + unsigned long cmd_last_tx; + int req_status; + int req_result; +}; + +/* Opcode OCF */ +/* HCI recv control commands opcode */ +#define HCI_OCF_FM_ENABLE_RECV_REQ 0x0001 +#define HCI_OCF_FM_DISABLE_RECV_REQ 0x0002 +#define HCI_OCF_FM_GET_RECV_CONF_REQ 0x0003 +#define HCI_OCF_FM_SET_RECV_CONF_REQ 0x0004 +#define HCI_OCF_FM_SET_MUTE_MODE_REQ 0x0005 +#define HCI_OCF_FM_SET_STEREO_MODE_REQ 0x0006 +#define HCI_OCF_FM_SET_ANTENNA 0x0007 +#define HCI_OCF_FM_SET_SIGNAL_THRESHOLD 0x0008 +#define HCI_OCF_FM_GET_SIGNAL_THRESHOLD 0x0009 +#define HCI_OCF_FM_GET_STATION_PARAM_REQ 0x000A +#define HCI_OCF_FM_GET_PROGRAM_SERVICE_REQ 0x000B +#define HCI_OCF_FM_GET_RADIO_TEXT_REQ 0x000C +#define HCI_OCF_FM_GET_AF_LIST_REQ 0x000D +#define HCI_OCF_FM_SEARCH_STATIONS 0x000E +#define HCI_OCF_FM_SEARCH_RDS_STATIONS 0x000F +#define HCI_OCF_FM_SEARCH_STATIONS_LIST 0x0010 +#define HCI_OCF_FM_CANCEL_SEARCH 0x0011 +#define HCI_OCF_FM_RDS_GRP 0x0012 +#define HCI_OCF_FM_RDS_GRP_PROCESS 0x0013 +#define HCI_OCF_FM_EN_WAN_AVD_CTRL 0x0014 +#define HCI_OCF_FM_EN_NOTCH_CTRL 0x0015 +#define HCI_OCF_FM_SET_EVENT_MASK 0x0016 +#define HCI_OCF_FM_SET_CH_DET_THRESHOLD 0x0017 +#define HCI_OCF_FM_GET_CH_DET_THRESHOLD 0x0018 +#define HCI_OCF_FM_SET_BLND_TBL 0x001B +#define HCI_OCF_FM_GET_BLND_TBL 0x001C +/* HCI trans control commans opcode*/ +#define HCI_OCF_FM_ENABLE_TRANS_REQ 0x0001 +#define HCI_OCF_FM_DISABLE_TRANS_REQ 0x0002 +#define HCI_OCF_FM_GET_TRANS_CONF_REQ 0x0003 +#define HCI_OCF_FM_SET_TRANS_CONF_REQ 0x0004 +#define HCI_OCF_FM_RDS_RT_REQ 0x0008 +#define HCI_OCF_FM_RDS_PS_REQ 0x0009 + + +/* HCI common control commands opcode */ +#define HCI_OCF_FM_TUNE_STATION_REQ 0x0001 +#define HCI_OCF_FM_DEFAULT_DATA_READ 0x0002 +#define HCI_OCF_FM_DEFAULT_DATA_WRITE 0x0003 +#define HCI_OCF_FM_RESET 0x0004 +#define HCI_OCF_FM_GET_FEATURE_LIST 0x0005 +#define HCI_OCF_FM_DO_CALIBRATION 0x0006 +#define HCI_OCF_FM_SET_CALIBRATION 0x0007 +#define HCI_OCF_FM_SET_SPUR_TABLE 0x0008 +#define HCI_OCF_FM_GET_SPUR_TABLE 0x0009 + +/*HCI Status parameters commands*/ +#define HCI_OCF_FM_READ_GRP_COUNTERS 0x0001 + +/*HCI Diagnostic commands*/ +#define HCI_OCF_FM_PEEK_DATA 0x0002 +#define HCI_OCF_FM_POKE_DATA 0x0003 +#define HCI_OCF_FM_SSBI_PEEK_REG 0x0004 +#define HCI_OCF_FM_SSBI_POKE_REG 0x0005 +#define HCI_OCF_FM_STATION_DBG_PARAM 0x0007 +#define HCI_FM_SET_INTERNAL_TONE_GENRATOR 0x0008 + +/* Opcode OGF */ +#define HCI_OGF_FM_RECV_CTRL_CMD_REQ 0x0013 +#define HCI_OGF_FM_TRANS_CTRL_CMD_REQ 0x0014 +#define HCI_OGF_FM_COMMON_CTRL_CMD_REQ 0x0015 +#define HCI_OGF_FM_STATUS_PARAMETERS_CMD_REQ 0x0016 +#define HCI_OGF_FM_TEST_CMD_REQ 0x0017 +#define HCI_OGF_FM_DIAGNOSTIC_CMD_REQ 0x003F + +/* Command opcode pack/unpack */ +#define hci_opcode_pack(ogf, ocf) (short) ((ocf & 0x03ff)|(ogf << 10)) +#define hci_opcode_ogf(op) (op >> 10) +#define hci_opcode_ocf(op) (op & 0x03ff) +#define hci_recv_ctrl_cmd_op_pack(ocf) \ + (short) hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, ocf) +#define hci_trans_ctrl_cmd_op_pack(ocf) \ + (short) hci_opcode_pack(HCI_OGF_FM_TRANS_CTRL_CMD_REQ, ocf) +#define hci_common_cmd_op_pack(ocf) \ + (short) hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ, ocf) +#define hci_status_param_op_pack(ocf) \ + (short) hci_opcode_pack(HCI_OGF_FM_STATUS_PARAMETERS_CMD_REQ, ocf) +#define hci_diagnostic_cmd_op_pack(ocf) \ + (short) hci_opcode_pack(HCI_OGF_FM_DIAGNOSTIC_CMD_REQ, ocf) + + +/* HCI commands with no arguments*/ +#define HCI_FM_ENABLE_RECV_CMD 1 +#define HCI_FM_DISABLE_RECV_CMD 2 +#define HCI_FM_GET_RECV_CONF_CMD 3 +#define HCI_FM_GET_STATION_PARAM_CMD 4 +#define HCI_FM_GET_SIGNAL_TH_CMD 5 +#define HCI_FM_GET_PROGRAM_SERVICE_CMD 6 +#define HCI_FM_GET_RADIO_TEXT_CMD 7 +#define HCI_FM_GET_AF_LIST_CMD 8 +#define HCI_FM_CANCEL_SEARCH_CMD 9 +#define HCI_FM_RESET_CMD 10 +#define HCI_FM_GET_FEATURES_CMD 11 +#define HCI_FM_STATION_DBG_PARAM_CMD 12 +#define HCI_FM_ENABLE_TRANS_CMD 13 +#define HCI_FM_DISABLE_TRANS_CMD 14 +#define HCI_FM_GET_TX_CONFIG 15 +#define HCI_FM_GET_DET_CH_TH_CMD 16 +#define HCI_FM_GET_BLND_TBL_CMD 17 + +/* Defines for FM TX*/ +#define TX_PS_DATA_LENGTH 108 +#define TX_RT_DATA_LENGTH 64 +#define PS_STRING_LEN 9 + +/* ----- HCI Command request ----- */ +struct hci_fm_recv_conf_req { + char emphasis; + char ch_spacing; + char rds_std; + char hlsi; + int band_low_limit; + int band_high_limit; +} ; + +/* ----- HCI Command request ----- */ +struct hci_fm_trans_conf_req_struct { + char emphasis; + char rds_std; + int band_low_limit; + int band_high_limit; +} ; + + +/* ----- HCI Command request ----- */ +struct hci_fm_tx_ps { + char ps_control; + short pi; + char pty; + char ps_repeatcount; + char ps_num; + char ps_data[TX_PS_DATA_LENGTH]; +} ; + +struct hci_fm_tx_rt { + char rt_control; + short pi; + char pty; + char rt_len; + char rt_data[TX_RT_DATA_LENGTH]; +} ; + +struct hci_fm_mute_mode_req { + char hard_mute; + char soft_mute; +} ; + +struct hci_fm_stereo_mode_req { + char stereo_mode; + char sig_blend; + char intf_blend; + char most_switch; +} ; + +struct hci_fm_search_station_req { + char srch_mode; + char scan_time; + char srch_dir; +} ; + +struct hci_fm_search_rds_station_req { + struct hci_fm_search_station_req srch_station; + char srch_pty; + short srch_pi; +} ; + +struct hci_fm_search_station_list_req { + char srch_list_mode; + char srch_list_dir; + int srch_list_max; + char srch_pty; +} ; + +struct hci_fm_rds_grp_req { + int rds_grp_enable_mask; + int rds_buf_size; + char en_rds_change_filter; +} ; + +struct hci_fm_en_avd_ctrl_req { + char no_freqs; + char freq_index; + char lo_shft; + short freq_min; + short freq_max; +} ; + +struct hci_fm_def_data_rd_req { + char mode; + char length; + char param_len; + char param; +} ; + +struct hci_fm_def_data_wr_req { + char mode; + char length; + char data[DEFAULT_DATA_SIZE]; +} ; + +struct hci_fm_riva_data { + char subopcode; + int start_addr; + char length; +} ; + +struct hci_fm_riva_poke { + struct hci_fm_riva_data cmd_params; + char data[MAX_RIVA_PEEK_RSP_SIZE]; +} ; + +struct hci_fm_ssbi_req { + short start_addr; + char data; +} ; +struct hci_fm_ssbi_peek { + short start_address; +} ; + +struct hci_fm_ch_det_threshold { + char sinr; + char sinr_samples; + char low_th; + char high_th; + +} ; + +struct hci_fm_blend_table { + char ucBlendType; + char ucBlendRampRateUp; + char ucBlendDebounceNumSampleUp; + char ucBlendDebounceIdxUp; + char ucBlendSinrIdxSkipStep; + char scBlendSinrHi; + char scBlendRmssiHi; + char ucBlendIndexHi; + char ucBlendIndex[MAX_BLEND_INDEX]; +} ; + +/*HCI events*/ +#define HCI_EV_TUNE_STATUS 0x01 +#define HCI_EV_RDS_LOCK_STATUS 0x02 +#define HCI_EV_STEREO_STATUS 0x03 +#define HCI_EV_SERVICE_AVAILABLE 0x04 +#define HCI_EV_SEARCH_PROGRESS 0x05 +#define HCI_EV_SEARCH_RDS_PROGRESS 0x06 +#define HCI_EV_SEARCH_LIST_PROGRESS 0x07 +#define HCI_EV_RDS_RX_DATA 0x08 +#define HCI_EV_PROGRAM_SERVICE 0x09 +#define HCI_EV_RADIO_TEXT 0x0A +#define HCI_EV_FM_AF_LIST 0x0B +#define HCI_EV_TX_RDS_GRP_AVBLE 0x0C +#define HCI_EV_TX_RDS_GRP_COMPL 0x0D +#define HCI_EV_TX_RDS_CONT_GRP_COMPL 0x0E +#define HCI_EV_CMD_COMPLETE 0x0F +#define HCI_EV_CMD_STATUS 0x10 +#define HCI_EV_TUNE_COMPLETE 0x11 +#define HCI_EV_SEARCH_COMPLETE 0x12 +#define HCI_EV_SEARCH_RDS_COMPLETE 0x13 +#define HCI_EV_SEARCH_LIST_COMPLETE 0x14 + +#define HCI_REQ_DONE 0 +#define HCI_REQ_PEND 1 +#define HCI_REQ_CANCELED 2 +#define HCI_REQ_STATUS 3 + +#define MAX_RAW_RDS_GRPS 21 + +#define RDSGRP_DATA_OFFSET 0x1 + +/*RT PLUS*/ +#define DUMMY_CLASS 0 +#define RT_PLUS_LEN_1_TAG 3 +#define RT_ERT_FLAG_BIT 5 + +/*TAG1*/ +#define TAG1_MSB_OFFSET 3 +#define TAG1_MSB_MASK 7 +#define TAG1_LSB_OFFSET 5 +#define TAG1_POS_MSB_MASK 31 +#define TAG1_POS_MSB_OFFSET 1 +#define TAG1_POS_LSB_OFFSET 7 +#define TAG1_LEN_OFFSET 1 +#define TAG1_LEN_MASK 63 + +/*TAG2*/ +#define TAG2_MSB_OFFSET 5 +#define TAG2_MSB_MASK 1 +#define TAG2_LSB_OFFSET 3 +#define TAG2_POS_MSB_MASK 7 +#define TAG2_POS_MSB_OFFSET 3 +#define TAG2_POS_LSB_OFFSET 5 +#define TAG2_LEN_MASK 31 + +#define AGT_MASK 31 +/*Extract 5 left most bits of lsb of 2nd block*/ +#define AGT(x) (x & AGT_MASK) +/*16 bits of 4th block*/ +#define AID(lsb, msb) ((msb << 8) | (lsb)) +/*Extract 5 right most bits of msb of 2nd block*/ +#define GTC(blk2msb) (blk2msb >> 3) + +#define GRP_3A 0x6 +#define RT_PLUS_AID 0x4bd7 + +/*ERT*/ +#define ERT_AID 0x6552 +#define CARRIAGE_RETURN 0x000D +#define MAX_ERT_SEGMENT 31 +#define ERT_FORMAT_DIR_BIT 1 + +#define EXTRACT_BIT(data, bit_pos) ((data & (1 << bit_pos)) >> bit_pos) + +struct hci_ev_tune_status { + char sub_event; + int station_freq; + char serv_avble; + char rssi; + char stereo_prg; + char rds_sync_status; + char mute_mode; + char sinr; + char intf_det_th; +} ; + +struct rds_blk_data { + char rdsMsb; + char rdsLsb; + char blockStatus; +} ; + +struct rds_grp_data { + struct rds_blk_data rdsBlk[4]; +} ; + +struct hci_ev_rds_rx_data { + char num_rds_grps; + struct rds_grp_data rds_grp_data[MAX_RAW_RDS_GRPS]; +} ; + +struct hci_ev_prg_service { + short pi_prg_id; + char pty_prg_type; + char ta_prg_code_type; + char ta_ann_code_flag; + char ms_switch_code_flag; + char dec_id_ctrl_code_flag; + char ps_num; + char prg_service_name[119]; +} ; + +struct hci_ev_radio_text { + short pi_prg_id; + char pty_prg_type; + char ta_prg_code_type; + char txt_ab_flag; + char radio_txt[64]; +} ; + +struct hci_ev_af_list { + int tune_freq; + short pi_code; + char af_size; + char af_list[FM_AF_LIST_MAX_SIZE]; +} ; + +struct hci_ev_cmd_complete { + char num_hci_cmd_pkts; + short cmd_opcode; +} ; + +struct hci_ev_cmd_status { + char status; + char num_hci_cmd_pkts; + short status_opcode; +} ; + +struct hci_ev_srch_st { + int station_freq; + char rds_cap; + char pty; + short status_opcode; +} ; + +struct hci_ev_rel_freq { + char rel_freq_msb; + char rel_freq_lsb; + +} ; +struct hci_ev_srch_list_compl { + char num_stations_found; + struct hci_ev_rel_freq rel_freq[20]; +} ; + +/* ----- HCI Event Response ----- */ +struct hci_fm_conf_rsp { + char status; + struct hci_fm_recv_conf_req recv_conf_rsp; +} ; + +struct hci_fm_get_trans_conf_rsp { + char status; + struct hci_fm_trans_conf_req_struct trans_conf_rsp; +} ; +struct hci_fm_sig_threshold_rsp { + char status; + char sig_threshold; +} ; + +struct hci_fm_station_rsp { + struct hci_ev_tune_status station_rsp; +} ; + +struct hci_fm_prgm_srv_rsp { + char status; + struct hci_ev_prg_service prg_srv; +} ; + +struct hci_fm_radio_txt_rsp { + char status; + struct hci_ev_radio_text rd_txt; +} ; + +struct hci_fm_af_list_rsp { + char status; + struct hci_ev_af_list rd_txt; +} ; + +struct hci_fm_data_rd_rsp { + char status; + char ret_data_len; + char data[DEFAULT_DATA_SIZE]; +} ; + +struct hci_fm_feature_list_rsp { + char status; + char feature_mask; +} ; + +struct hci_fm_dbg_param_rsp { + char status; + char blend; + char soft_mute; + char inf_blend; + char inf_soft_mute; + char pilot_pil; + char io_verc; + char in_det_out; +} ; + +#define CLKSPURID_INDEX0 0 +#define CLKSPURID_INDEX1 5 +#define CLKSPURID_INDEX2 10 +#define CLKSPURID_INDEX3 15 +#define CLKSPURID_INDEX4 20 +#define CLKSPURID_INDEX5 25 + +#define MAX_SPUR_FREQ_LIMIT 30 +#define CKK_SPUR 0x3B +#define SPUR_DATA_SIZE 0x4 +#define SPUR_ENTRIES_PER_ID 0x5 + +#define COMPUTE_SPUR(val) ((((val) - (76000)) / (50))) +#define GET_FREQ(val, bit) ((bit == 1) ? ((val) >> 8) : ((val) & 0xFF)) +#define GET_SPUR_ENTRY_LEVEL(val) ((val) / (5)) + +struct hci_fm_spur_data { + int freq[MAX_SPUR_FREQ_LIMIT]; + char rmssi[MAX_SPUR_FREQ_LIMIT]; + char enable[MAX_SPUR_FREQ_LIMIT]; +} ; + + +/* HCI dev events */ +#define RADIO_HCI_DEV_REG 1 +#define RADIO_HCI_DEV_WRITE 2 + +#define hci_req_lock(d) mutex_lock(&d->req_lock) +#define hci_req_unlock(d) mutex_unlock(&d->req_lock) + +/* FM RDS */ +#define RDS_PTYPE 2 +#define RDS_PID_LOWER 1 +#define RDS_PID_HIGHER 0 +#define RDS_OFFSET 5 +#define RDS_PS_LENGTH_OFFSET 7 +#define RDS_STRING 8 +#define RDS_PS_DATA_OFFSET 8 +#define RDS_CONFIG_OFFSET 3 +#define RDS_AF_JUMP_OFFSET 4 +#define PI_CODE_OFFSET 4 +#define AF_SIZE_OFFSET 6 +#define AF_LIST_OFFSET 7 +#define RT_A_B_FLAG_OFFSET 4 +/*FM states*/ + +enum radio_state_t { + FM_OFF, + FM_RECV, + FM_TRANS, + FM_RESET, + FM_CALIB, + FM_TURNING_OFF, + FM_RECV_TURNING_ON, + FM_TRANS_TURNING_ON, + FM_MAX_NO_STATES, +}; + +enum emphasis_type { + FM_RX_EMP75 = 0x0, + FM_RX_EMP50 = 0x1 +}; + +enum channel_space_type { + FM_RX_SPACE_200KHZ = 0x0, + FM_RX_SPACE_100KHZ = 0x1, + FM_RX_SPACE_50KHZ = 0x2 +}; + +enum high_low_injection { + AUTO_HI_LO_INJECTION = 0x0, + LOW_SIDE_INJECTION = 0x1, + HIGH_SIDE_INJECTION = 0x2 +}; + +enum fm_rds_type { + FM_RX_RDBS_SYSTEM = 0x0, + FM_RX_RDS_SYSTEM = 0x1 +}; + +enum hlm_region_t { + HELIUM_REGION_US, + HELIUM_REGION_EU, + HELIUM_REGION_JAPAN, + HELIUM_REGION_JAPAN_WIDE, + HELIUM_REGION_OTHER +}; + +/* Search options */ +enum search_t { + SEEK, + SCAN, + SCAN_FOR_STRONG, + SCAN_FOR_WEAK, + RDS_SEEK_PTY, + RDS_SCAN_PTY, + RDS_SEEK_PI, + RDS_AF_JUMP, +}; + +enum spur_entry_levels { + ENTRY_0, + ENTRY_1, + ENTRY_2, + ENTRY_3, + ENTRY_4, + ENTRY_5, +}; + +/* Band limits */ +#define REGION_US_EU_BAND_LOW 87500 +#define REGION_US_EU_BAND_HIGH 108000 +#define REGION_JAPAN_STANDARD_BAND_LOW 76000 +#define REGION_JAPAN_STANDARD_BAND_HIGH 90000 +#define REGION_JAPAN_WIDE_BAND_LOW 90000 +#define REGION_JAPAN_WIDE_BAND_HIGH 108000 + +#define SRCH_MODE 0x07 +#define SRCH_DIR 0x08 /* 0-up 1-down */ +#define SCAN_DWELL 0x70 +#define SRCH_ON 0x80 + +/* I/O Control */ +#define IOC_HRD_MUTE 0x03 +#define IOC_SFT_MUTE 0x01 +#define IOC_MON_STR 0x01 +#define IOC_SIG_BLND 0x01 +#define IOC_INTF_BLND 0x01 +#define IOC_ANTENNA 0x01 + +/* RDS Control */ +#define RDS_ON 0x01 +#define RDS_BUF_SZ 100 + +/* constants */ +#define RDS_BLOCKS_NUM (4) +#define BYTES_PER_BLOCK (3) +#define MAX_PS_LENGTH (108) +#define MAX_RT_LENGTH (64) +#define RDS_GRP_CNTR_LEN (36) +#define RX_RT_DATA_LENGTH (63) +/* Search direction */ +#define SRCH_DIR_UP (0) +#define SRCH_DIR_DOWN (1) + +/*Search RDS stations*/ +#define SEARCH_RDS_STNS_MODE_OFFSET 4 + +/*Search Station list */ +#define PARAMS_PER_STATION 0x08 +#define STN_NUM_OFFSET 0x01 +#define STN_FREQ_OFFSET 0x02 +#define KHZ_TO_MHZ 1000 +#define GET_MSB(x)((x >> 8) & 0xFF) +#define GET_LSB(x)((x) & 0xFF) + +/* control options */ +#define CTRL_ON (1) +#define CTRL_OFF (0) + +/*Diagnostic commands*/ + +#define RIVA_PEEK_OPCODE 0x0D +#define RIVA_POKE_OPCODE 0x0C + +#define PEEK_DATA_OFSET 0x1 +#define RIVA_PEEK_PARAM 0x6 +#define RIVA_PEEK_LEN_OFSET 0x6 +#define SSBI_PEEK_LEN 0x01 +/*Calibration data*/ +#define PROCS_CALIB_MODE 1 +#define PROCS_CALIB_SIZE 23 +#define DC_CALIB_MODE 2 +#define DC_CALIB_SIZE 48 +#define RSB_CALIB_MODE 3 +#define RSB_CALIB_SIZE 4 +#define CALIB_DATA_OFSET 2 +#define CALIB_MODE_OFSET 1 +#define MAX_CALIB_SIZE 75 + +/* Channel validity */ +#define INVALID_CHANNEL (0) +#define VALID_CHANNEL (1) + +struct hci_fm_set_cal_req_proc { + char mode; + /*Max process calibration data size*/ + char data[PROCS_CALIB_SIZE]; +} ; + +struct hci_fm_set_cal_req_dc { + char mode; + /*Max DC calibration data size*/ + char data[DC_CALIB_SIZE]; +} ; + +struct hci_cc_do_calibration_rsp { + char status; + char mode; + char data[MAX_CALIB_SIZE]; +} ; + +struct hci_fm_set_spur_table_req { + char mode; + char no_of_freqs_entries; + char spur_data[FM_SPUR_TBL_SIZE]; +} ; +/* Low Power mode*/ +#define SIG_LEVEL_INTR (1 << 0) +#define RDS_SYNC_INTR (1 << 1) +#define AUDIO_CTRL_INTR (1 << 2) +#define AF_JUMP_ENABLE (1 << 4) + +int hci_def_data_read(struct hci_fm_def_data_rd_req *arg, + struct radio_hci_dev *hdev); +int hci_def_data_write(struct hci_fm_def_data_wr_req *arg, + struct radio_hci_dev *hdev); +int hci_fm_do_calibration(char *arg, struct radio_hci_dev *hdev); +int hci_fm_do_calibration(char *arg, struct radio_hci_dev *hdev); + +static inline int is_valid_tone(int tone) +{ + if ((tone >= MIN_TX_TONE_VAL) && + (tone <= MAX_TX_TONE_VAL)) + return 1; + else + return 0; +} + +static inline int is_valid_hard_mute(int hard_mute) +{ + if ((hard_mute >= MIN_HARD_MUTE_VAL) && + (hard_mute <= MAX_HARD_MUTE_VAL)) + return 1; + else + return 0; +} + +static inline int is_valid_srch_mode(int srch_mode) +{ + if ((srch_mode >= MIN_SRCH_MODE) && + (srch_mode <= MAX_SRCH_MODE)) + return 1; + else + return 0; +} + +static inline int is_valid_scan_dwell_prd(int scan_dwell_prd) +{ + if ((scan_dwell_prd >= MIN_SCAN_DWELL) && + (scan_dwell_prd <= MAX_SCAN_DWELL)) + return 1; + else + return 0; +} + +static inline int is_valid_sig_th(int sig_th) +{ + if ((sig_th >= MIN_SIG_TH) && + (sig_th <= MAX_SIG_TH)) + return 1; + else + return 0; +} + +static inline int is_valid_pty(int pty) +{ + if ((pty >= MIN_PTY) && + (pty <= MAX_PTY)) + return 1; + else + return 0; +} + +static inline int is_valid_pi(int pi) +{ + if ((pi >= MIN_PI) && + (pi <= MAX_PI)) + return 1; + else + return 0; +} + +static inline int is_valid_srch_station_cnt(int cnt) +{ + if ((cnt >= MIN_SRCH_STATIONS_CNT) && + (cnt <= MAX_SRCH_STATIONS_CNT)) + return 1; + else + return 0; +} + +static inline int is_valid_chan_spacing(int spacing) +{ + if ((spacing >= MIN_CHAN_SPACING) && + (spacing <= MAX_CHAN_SPACING)) + return 1; + else + return 0; +} + +static inline int is_valid_emphasis(int emphasis) +{ + if ((emphasis >= MIN_EMPHASIS) && + (emphasis <= MAX_EMPHASIS)) + return 1; + else + return 0; +} + +static inline int is_valid_rds_std(int rds_std) +{ + if ((rds_std >= MIN_RDS_STD) && + (rds_std <= MAX_RDS_STD)) + return 1; + else + return 0; +} + +static inline int is_valid_antenna(int antenna_type) +{ + if ((antenna_type >= MIN_ANTENNA_VAL) && + (antenna_type <= MAX_ANTENNA_VAL)) + return 1; + else + return 0; +} + +static inline int is_valid_ps_repeat_cnt(int cnt) +{ + if ((cnt >= MIN_TX_PS_REPEAT_CNT) && + (cnt <= MAX_TX_PS_REPEAT_CNT)) + return 1; + else + return 0; +} + +static inline int is_valid_soft_mute(int soft_mute) +{ + if ((soft_mute >= MIN_SOFT_MUTE) && + (soft_mute <= MAX_SOFT_MUTE)) + return 1; + else + return 0; +} + +static inline int is_valid_peek_len(int len) +{ + if ((len >= MIN_PEEK_ACCESS_LEN) && + (len <= MAX_PEEK_ACCESS_LEN)) + return 1; + else + return 0; +} + +static inline int is_valid_reset_cntr(int cntr) +{ + if ((cntr >= MIN_RESET_CNTR) && + (cntr <= MAX_RESET_CNTR)) + return 1; + else + return 0; +} + +static inline int is_valid_hlsi(int hlsi) +{ + if ((hlsi >= MIN_HLSI) && + (hlsi <= MAX_HLSI)) + return 1; + else + return 0; +} + +static inline int is_valid_notch_filter(int filter) +{ + if ((filter >= MIN_NOTCH_FILTER) && + (filter <= MAX_NOTCH_FILTER)) + return 1; + else + return 0; +} + +static inline int is_valid_intf_det_low_th(int th) +{ + if ((th >= MIN_INTF_DET_OUT_LW_TH) && + (th <= MAX_INTF_DET_OUT_LW_TH)) + return 1; + else + return 0; +} + +static inline int is_valid_intf_det_hgh_th(int th) +{ + if ((th >= MIN_INTF_DET_OUT_HG_TH) && + (th <= MAX_INTF_DET_OUT_HG_TH)) + return 1; + else + return 0; +} + +static inline int is_valid_sinr_th(int th) +{ + if ((th >= MIN_SINR_TH) && + (th <= MAX_SINR_TH)) + return 1; + else + return 0; +} + +static inline int is_valid_sinr_samples(int samples_cnt) +{ + if ((samples_cnt >= MIN_SINR_SAMPLES) && + (samples_cnt <= MAX_SINR_SAMPLES)) + return 1; + else + return 0; +} + +static inline int is_valid_fm_state(int state) +{ + if ((state >= 0) && (state < FM_MAX_NO_STATES)) + return 1; + else + return 0; +} + +static inline int is_valid_blend_value(int val) +{ + if ((val >= MIN_BLEND_HI) && (val <= MAX_BLEND_HI)) + return 1; + else + return 0; +} + +struct helium_device { + int tune_req; + unsigned int mode; + short pi; + char pty; + char ps_repeatcount; + char prev_trans_rds; + char af_jump_bit; + struct hci_fm_mute_mode_req mute_mode; + struct hci_fm_stereo_mode_req stereo_mode; + struct hci_fm_station_rsp fm_st_rsp; + struct hci_fm_search_station_req srch_st; + struct hci_fm_search_rds_station_req srch_rds; + struct hci_fm_search_station_list_req srch_st_list; + struct hci_fm_recv_conf_req recv_conf; + struct hci_fm_trans_conf_req_struct trans_conf; + struct hci_fm_rds_grp_req rds_grp; + unsigned char g_search_mode; + unsigned char power_mode; + int search_on; + unsigned char spur_table_size; + unsigned char g_scan_time; + unsigned int g_antenna; + unsigned int g_rds_grp_proc_ps; + unsigned char event_mask; + enum hlm_region_t region; + struct hci_fm_dbg_param_rsp st_dbg_param; + struct hci_ev_srch_list_compl srch_st_result; +}; +#endif /* __UAPI_RADIO_HCI_CORE_H */ diff --git a/helium/radio_helium_hal.c b/helium/radio_helium_hal.c new file mode 100644 index 0000000..ce39b59 --- /dev/null +++ b/helium/radio_helium_hal.c @@ -0,0 +1,1067 @@ +/* +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 <utils/Log.h> +#include "radio-helium-commands.h" +#include "radio-helium.h" +#include "fm_hci.h" +#include <dlfcn.h> + +fm_vendor_callbacks_t *jni_cb; +int hci_fm_get_signal_threshold(); +int hci_fm_enable_recv_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; + +#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(); +} + +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; + + if (ev_buff == NULL) { + ALOGE("%s:%s, buffer is null\n", LOG_TAG, __func__); + return; + } + status = (char) *ev_buff; + radio_hci_req_complete(status); + if (radio->mode == FM_TURNING_OFF) { + jni_cb->disabled_cb(); + radio->mode = FM_OFF; + } +} + +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 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; + } + ALOGE("%s:buff[1] = 0x%x buff[2] = 0x%x", LOG_TAG, buff[1], buff[2]); + opcode = ((buff[2] << 8) | buff[1]); + ALOGE("%s: Received HCI CMD COMPLETE EVENT for 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): + case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_CH_DET_THRESHOLD): + case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_BLND_TBL): + case hci_common_cmd_op_pack(HCI_OCF_FM_DEFAULT_DATA_WRITE): + hci_cc_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_common_cmd_op_pack(HCI_OCF_FM_GET_SPUR_TABLE): + hci_cc_get_spur_tbl(buff); + break; + case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_SSBI_PEEK_REG): + hci_cc_ssbi_peek_rsp(buff); + break; + case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_SIGNAL_THRESHOLD): + hci_cc_sig_threshold_rsp(buff); + break; + + case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_STATION_PARAM_REQ): + hci_cc_station_rsp(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_DEFAULT_DATA_READ): + hci_cc_riva_read_default_rsp(buff); + break; + + case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_PEEK_DATA): + hci_cc_riva_peek_rsp(buff); + break; + + case hci_common_cmd_op_pack(HCI_OCF_FM_GET_FEATURE_LIST): + hci_cc_feature_list_rsp(buff); + break; + + case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_STATION_DBG_PARAM): + hci_cc_dbg_param_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; + + case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_CH_DET_THRESHOLD): + hci_cc_get_ch_det_threshold_rsp(buff); + break; + case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_BLND_TBL): + hci_cc_get_blend_tbl_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++; + 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 void hci_ev_rt_plus(struct rds_grp_data rds_buf) +{ + char tag_type1, tag_type2; + char *data = NULL; + int len = 0; + unsigned short int agt; + + agt = AGT(rds_buf.rdsBlk[1].rdsLsb); + /*right most 3 bits of Lsb of block 2 + * and left most 3 bits of Msb of block 3 + */ + tag_type1 = (((agt & TAG1_MSB_MASK) << TAG1_MSB_OFFSET) | + (rds_buf.rdsBlk[2].rdsMsb >> TAG1_LSB_OFFSET)); + + /*right most 1 bit of lsb of 3rd block + * and left most 5 bits of Msb of 4th block + */ + tag_type2 = (((rds_buf.rdsBlk[2].rdsLsb & TAG2_MSB_MASK) << TAG2_MSB_OFFSET) | + (rds_buf.rdsBlk[3].rdsMsb >> TAG2_LSB_OFFSET)); + + if (tag_type1 != DUMMY_CLASS) + len += RT_PLUS_LEN_1_TAG; + if (tag_type2 != DUMMY_CLASS) + len += RT_PLUS_LEN_1_TAG; + + if (len != 0) { + len += 2; + data = malloc(len); + } else { + ALOGE("%s:Len is zero\n", LOG_TAG); + return ; + } + if (data != NULL) { + data[0] = len; + len = 1; + data[len++] = rt_ert_flag; + if (tag_type1 != DUMMY_CLASS) { + data[len++] = tag_type1; + /*start position of tag1 + *right most 5 bits of msb of 3rd block + *and left most bit of lsb of 3rd block + */ + data[len++] = (((rds_buf.rdsBlk[2].rdsMsb & TAG1_POS_MSB_MASK) + << TAG1_POS_MSB_OFFSET)| + (rds_buf.rdsBlk[2].rdsLsb >> TAG1_POS_LSB_OFFSET)); + /*length of tag1 + *left most 6 bits of lsb of 3rd block + */ + data[len++] = ((rds_buf.rdsBlk[2].rdsLsb >> TAG1_LEN_OFFSET) & + TAG1_LEN_MASK) + 1; + } + if (tag_type2 != DUMMY_CLASS) { + data[len++] = tag_type2; + /*start position of tag2 + *right most 3 bit of msb of 4th block + *and left most 3 bits of lsb of 4th block + */ + data[len++] = (((rds_buf.rdsBlk[3].rdsMsb & TAG2_POS_MSB_MASK) + << TAG2_POS_MSB_OFFSET) | + (rds_buf.rdsBlk[3].rdsLsb >> TAG2_POS_LSB_OFFSET)); + /*length of tag2 + *right most 5 bits of lsb of 4th block + */ + data[len++] = (rds_buf.rdsBlk[3].rdsLsb & TAG2_LEN_MASK) + 1; + } + jni_cb->rt_plus_update_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_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) + hci_buff_ert(&temp); + } +} + +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; + 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; + } + 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__); + 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; + + 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: + radio->g_search_mode = val; + break; + case HCI_FM_HELIUM_SCANDWELL: + radio->g_scan_time = val; + 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: + radio->srch_rds.srch_pty = val; + radio->srch_st_list.srch_pty = val; + break; + case HCI_FM_HELIUM_SRCH_PI: + radio->srch_rds.srch_pi = val; + break; + case HCI_FM_HELIUM_SRCH_CNT: + radio->srch_st_list.srch_list_max = val; + 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; + radio->g_rds_grp_proc_ps = (rds_grps_proc >> RDS_CONFIG_OFFSET); + 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_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; + 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 void get_fm_ctrl(int cmd, int val) +{ + int ret = 0; + + 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; + 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 +}; diff --git a/helium/radio_helium_hal_cmds.c b/helium/radio_helium_hal_cmds.c new file mode 100644 index 0000000..8e69e4b --- /dev/null +++ b/helium/radio_helium_hal_cmds.c @@ -0,0 +1,256 @@ +/* +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 <utils/Log.h> +#include "radio-helium-commands.h" +#include "radio-helium.h" +#include "fm_hci.h" +#include <dlfcn.h> +#define LOG_TAG "radio_helium" + +static int send_fm_cmd_pkt(uint16_t opcode, uint32_t len, void *param) +{ + int p_len = 4 + len; + int ret = 0; + +// pthread_mutex_lock(&fm_hal); + FM_HDR *hdr = (FM_HDR *) malloc(p_len); + if (!hdr) { + ALOGE("%s:hdr allocation failed", LOG_TAG); + return -1; + } + + ALOGE("%s:%s: Sizeof FM_HDR: %d", LOG_TAG, __func__, sizeof(FM_HDR)); + ALOGE("%s:opcode: %x", LOG_TAG, opcode); + + hdr->protocol_byte = 0x11; + hdr->opcode = opcode; + hdr->plen = len; + if (len) + memcpy(hdr->cmd_params, (uint8_t *)param, len); + ALOGE("%s:calling transmit", __func__); + transmit(hdr); + ALOGE("%s:transmit success",__func__); + return 0; +} + +int hci_fm_get_signal_threshold() +{ + + FM_HDR *hdr = (FM_HDR *) malloc(sizeof(FM_HDR)); + hdr->protocol_byte = FM_CMD; + hdr->opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, HCI_OCF_FM_GET_SIGNAL_THRESHOLD); + hdr->plen = 0; + transmit(hdr); + return 0; +} + +int hci_fm_enable_recv_req() +{ + uint16_t opcode = 0; + + opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, + HCI_OCF_FM_ENABLE_RECV_REQ); + return send_fm_cmd_pkt(opcode, 0, NULL); +} + +int hci_fm_disable_recv_req() +{ + uint16_t opcode = 0; + + opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, + HCI_OCF_FM_DISABLE_RECV_REQ); + return send_fm_cmd_pkt(opcode, 0, NULL); +} + +int hci_fm_mute_mode_req(struct hci_fm_mute_mode_req *mute) +{ + uint16_t opcode = 0; + int len = 0; + opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, + HCI_OCF_FM_SET_MUTE_MODE_REQ); + len = sizeof(struct hci_fm_mute_mode_req); + return send_fm_cmd_pkt(opcode, len, mute); +} + +int helium_search_list(struct hci_fm_search_station_list_req *s_list) +{ + uint16_t opcode = 0; + + if (s_list == NULL) { + ALOGE("%s:%s, search list param is null\n", LOG_TAG, __func__); + return -1; + } + opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, + HCI_OCF_FM_SEARCH_STATIONS_LIST); + return send_fm_cmd_pkt(opcode, sizeof((*s_list)), s_list); +} + +int helium_search_rds_stations(struct hci_fm_search_rds_station_req *rds_srch) +{ + uint16_t opcode = 0; + + if (rds_srch == NULL) { + ALOGE("%s:%s, rds stations param is null\n", LOG_TAG, __func__); + return -1; + } + opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, + HCI_OCF_FM_SEARCH_RDS_STATIONS); + return send_fm_cmd_pkt(opcode, sizeof((*rds_srch)), rds_srch); +} + +int helium_search_stations(struct hci_fm_search_station_req *srch) +{ + uint16_t opcode = 0; + + if (srch == NULL) { + ALOGE("%s:%s, search station param is null\n", LOG_TAG, __func__); + return -1; + } + opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, + HCI_OCF_FM_SEARCH_STATIONS); + return send_fm_cmd_pkt(opcode, sizeof((*srch)), srch); +} + +int helium_cancel_search_req() +{ + uint16_t opcode = 0; + + opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, + HCI_OCF_FM_CANCEL_SEARCH); + return send_fm_cmd_pkt(opcode, 0, NULL); +} + +int hci_fm_set_recv_conf_req (struct hci_fm_recv_conf_req *conf) +{ + uint16_t opcode = 0; + + if (conf == NULL) { + ALOGE("%s:%s, recv conf is null\n", LOG_TAG, __func__); + return -1; + } + opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, + HCI_OCF_FM_SET_RECV_CONF_REQ); + return send_fm_cmd_pkt(opcode, sizeof((*conf)), conf); +} + +int helium_set_sig_threshold_req(char th) +{ + uint16_t opcode = 0; + + if (th == NULL) { + ALOGE("%s:Threshold value NULL", LOG_TAG); + return -1; + } + opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, + HCI_OCF_FM_SET_SIGNAL_THRESHOLD); + return send_fm_cmd_pkt(opcode, sizeof(th), th); +} + +int helium_rds_grp_mask_req(struct hci_fm_rds_grp_req *rds_grp_msk) +{ + uint16_t opcode = 0; + + if (rds_grp_msk == NULL) { + ALOGE("%s:%s, grp mask param is null\n", LOG_TAG, __func__); + return -EINVAL; + } + opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, + HCI_OCF_FM_RDS_GRP); + return send_fm_cmd_pkt(opcode, sizeof(*rds_grp_msk), rds_grp_msk); +} + +int helium_rds_grp_process_req(int rds_grp) +{ + uint16_t opcode = 0; + + opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, + HCI_OCF_FM_RDS_GRP_PROCESS); + return send_fm_cmd_pkt(opcode, sizeof(rds_grp), &rds_grp); +} + +int helium_set_event_mask_req(char e_mask) +{ + uint16_t opcode = 0; + + opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, + HCI_OCF_FM_SET_EVENT_MASK); + return send_fm_cmd_pkt(opcode, sizeof(e_mask), &e_mask); +} + +int helium_set_antenna_req(char ant) +{ + uint16_t opcode = 0; + + opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, + HCI_OCF_FM_SET_ANTENNA); + return send_fm_cmd_pkt(opcode, sizeof(ant), &ant); +} + +int helium_set_fm_mute_mode_req(struct hci_fm_mute_mode_req *mute) +{ + uint16_t opcode = 0; + + if (mute == NULL) { + ALOGE("%s:%s, mute mode is null\n", LOG_TAG, __func__); + return -1; + } + opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, + HCI_OCF_FM_SET_MUTE_MODE_REQ); + return send_fm_cmd_pkt(opcode, sizeof((*mute)), mute); +} + +int hci_fm_tune_station_req(int param) +{ + uint16_t opcode = 0; + int tune_freq = param; + + ALOGE("%s:tune_freq: %d", LOG_TAG, tune_freq); + opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ, + HCI_OCF_FM_TUNE_STATION_REQ); + return send_fm_cmd_pkt(opcode, sizeof(tune_freq), &tune_freq); +} + +int hci_set_fm_stereo_mode_req(struct hci_fm_stereo_mode_req *param) +{ + uint16_t opcode = 0; + struct hci_fm_stereo_mode_req *stereo_mode_req = + (struct hci_fm_stereo_mode_req *) param; + + if (stereo_mode_req == NULL) { + ALOGE("%s:%s, stere mode req is null\n", LOG_TAG, __func__); + return -1; + } + opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ, + HCI_OCF_FM_SET_STEREO_MODE_REQ); + return send_fm_cmd_pkt(opcode, sizeof((*stereo_mode_req)), + stereo_mode_req); +} + diff --git a/jni/Android.mk b/jni/Android.mk index 5bc46d3..b554b40 100644 --- a/jni/Android.mk +++ b/jni/Android.mk @@ -10,7 +10,9 @@ ConfigFmThs.cpp \ FmIoctlsInterface.cpp \ FmPerformanceParams.cpp +LOCAL_LDLIBS += -ldl LOCAL_SHARED_LIBRARIES := \ + libandroid_runtime \ libnativehelper \ libcutils diff --git a/jni/FmConst.h b/jni/FmConst.h index 00e6f82..8c8d6f4 100644 --- a/jni/FmConst.h +++ b/jni/FmConst.h @@ -145,5 +145,13 @@ enum FM_V4L2_PRV_CONTROLS V4L2_CID_PRV_IRIS_READ_DEFAULT = V4L2_CTRL_CLASS_USER + 0x928, V4L2_CID_PRV_IRIS_WRITE_DEFAULT, V4L2_CID_PRV_SET_CALIBRATION = V4L2_CTRL_CLASS_USER + 0x92A, + HCI_FM_HELIUM_SET_SPURTABLE = 0x0098092D, + HCI_FM_HELIUM_GET_SPUR_TBL = 0x0098092E, + V4L2_CID_PRV_IRIS_FREQ, + V4L2_CID_PRV_IRIS_SEEK, + V4L2_CID_PRV_IRIS_UPPER_BAND, + V4L2_CID_PRV_IRIS_LOWER_BAND, + V4L2_CID_PRV_IRIS_AUDIO_MODE, }; + #endif diff --git a/jni/android_hardware_fm.cpp b/jni/android_hardware_fm.cpp index 9eea048..8663bfe 100644 --- a/jni/android_hardware_fm.cpp +++ b/jni/android_hardware_fm.cpp @@ -30,20 +30,32 @@ #include "jni.h" #include "JNIHelp.h" -#include "android_runtime/AndroidRuntime.h" #include "utils/Log.h" #include "utils/misc.h" #include "FmIoctlsInterface.h" #include "ConfigFmThs.h" #include <cutils/properties.h> #include <fcntl.h> +#include <math.h> #include <sys/ioctl.h> #include <linux/videodev2.h> -#include <math.h> +#include <assert.h> +#include <dlfcn.h> +#include "android_runtime/Log.h" +#include "android_runtime/AndroidRuntime.h" #define RADIO "/dev/radio0" #define FM_JNI_SUCCESS 0L #define FM_JNI_FAILURE -1L + +static JNIEnv *g_jEnv = NULL; +static JavaVM *g_jVM = NULL; + +namespace android { +char *FM_LIBRARY_NAME = "fm_helium.so"; +char *FM_LIBRARY_SYMBOL_NAME = "FM_HELIUM_LIB_INTERFACE"; +void *lib_handle; + #define SEARCH_DOWN 0 #define SEARCH_UP 1 #define HIGH_BAND 2 @@ -74,9 +86,296 @@ enum search_dir_t { SCAN_UP, SCAN_DN }; +typedef void (*enb_result_cb)(); +typedef void (*tune_rsp_cb)(int Freq); +typedef void (*seek_rsp_cb)(int Freq); +typedef void (*scan_rsp_cb)(); +typedef void (*srch_list_rsp_cb)(uint16_t *scan_tbl); +typedef void (*stereo_mode_cb)(bool status); +typedef void (*rds_avl_sts_cb)(bool status); +typedef void (*af_list_cb)(uint16_t *af_list); +typedef void (*rt_cb)(char *rt); +typedef void (*ps_cb)(char *ps); +typedef void (*oda_cb)(); +typedef void (*rt_plus_cb)(char *rt_plus); +typedef void (*ert_cb)(char *ert); +typedef void (*disable_cb)(); +typedef void (*callback_thread_event)(unsigned int evt); + + +static JNIEnv *mCallbackEnv = NULL; +static jobject mCallbacksObj = NULL; +static jfieldID sCallbacksField; + +jclass javaClassRef; +static jmethodID method_psInfoCallback; +static jmethodID method_rtCallback; +static jmethodID method_ertCallback; +static jmethodID method_aflistCallback; +static jmethodID method_rtplusCallback; + +jmethodID method_enableCallback; +jmethodID method_tuneCallback; +jmethodID method_seekCmplCallback; +jmethodID method_scanNxtCallback; +jmethodID method_srchListCallback; +jmethodID method_stereostsCallback; +jmethodID method_rdsAvlStsCallback; +jmethodID method_disableCallback; + +static bool checkCallbackThread() { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + ALOGE("Callback env check fail: env: %p, callback: %p", env, mCallbackEnv); + if (mCallbackEnv != env || mCallbackEnv == NULL) + { + ALOGE("Callback env check fail: env: %p, callback: %p", env, mCallbackEnv); + return false; + } + return true; +} +void fm_enabled_cb() { + ALOGE("Entered %s", __func__); + if (mCallbackEnv != NULL) { + ALOGE("javaObjectRef creating"); + jobject javaObjectRef = mCallbackEnv->NewObject(javaClassRef, method_enableCallback); + mCallbacksObj = javaObjectRef; + ALOGE("javaObjectRef = %p mCallbackobject =%p \n",javaObjectRef,mCallbacksObj); + } + ALOGE("exit %s", __func__); +} + +void fm_tune_cb(int Freq) +{ + ALOGE("TUNE:Freq:%d", Freq); + mCallbackEnv->CallVoidMethod(mCallbacksObj, method_tuneCallback, (jint) Freq); +} + +void fm_seek_cmpl_cb(int Freq) +{ + ALOGE("SEEK_CMPL: Freq: %d", Freq); + mCallbackEnv->CallVoidMethod(mCallbacksObj, method_seekCmplCallback, (jint) Freq); +} + +void fm_scan_next_cb() +{ + ALOGE("SCAN_NEXT"); + mCallbackEnv->CallVoidMethod(mCallbacksObj, method_scanNxtCallback); +} + +void fm_srch_list_cb(uint16_t *scan_tbl) +{ + ALOGE("SRCH_LIST"); + //mCallbackEnv->CallVoidMethod(javaObjectRef, method_srchListCallback); +} + +void fm_stereo_status_cb(bool stereo) +{ + ALOGE("STEREO: %d", stereo); + mCallbackEnv->CallVoidMethod(mCallbacksObj, method_stereostsCallback, (jboolean) stereo); +} + +void fm_rds_avail_status_cb(bool rds_avl) +{ + ALOGE("fm_rds_avail_status_cb: %d", rds_avl); + mCallbackEnv->CallVoidMethod(mCallbacksObj, method_rdsAvlStsCallback, (jboolean) rds_avl); +} + +void fm_af_list_update_cb(uint16_t *af_list) +{ + ALOGE("AF_LIST"); + jbyteArray af_buffer = NULL; + + if (!checkCallbackThread()) { + ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); + return; + } + + af_buffer = mCallbackEnv->NewByteArray(STD_BUF_SIZE); + if (af_buffer == NULL) { + ALOGE(" af list allocate failed :"); + return; + } + + mCallbackEnv->SetByteArrayRegion(af_buffer, 0, STD_BUF_SIZE,(jbyte *)af_list); + mCallbackEnv->CallVoidMethod(mCallbacksObj, method_aflistCallback,af_buffer); + mCallbackEnv->DeleteLocalRef(af_buffer); +} + +void fm_rt_update_cb(char *rt) +{ + ALOGE("RT_EVT: " ); + jbyteArray rt_buff = NULL; + int i,len; + + if (!checkCallbackThread()) { + ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); + return; + } + + len = (int)(rt[0] & 0x0F); + len = len+5; + + ALOGE(" rt data len=%d :",len); + rt_buff = mCallbackEnv->NewByteArray(len); + if (rt_buff == NULL) { + ALOGE(" ps data allocate failed :"); + return; + } + + mCallbackEnv->SetByteArrayRegion(rt_buff, 0, len,(jbyte *)rt); + jbyte* bytes= mCallbackEnv->GetByteArrayElements(rt_buff,0); + + mCallbackEnv->CallVoidMethod(mCallbacksObj, method_rtCallback,rt_buff); + mCallbackEnv->DeleteLocalRef(rt_buff); +} + +void fm_ps_update_cb(char *ps) +{ + jbyteArray ps_data = NULL; + int i,len; + int numPs; + if (!checkCallbackThread()) { + ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); + return; + } + + numPs = (int)(ps[0] & 0x0F); + len = (numPs *8)+5; + + ALOGE(" ps data len=%d :",len); + ps_data = mCallbackEnv->NewByteArray(len); + if(ps_data == NULL) { + ALOGE(" ps data allocate failed :"); + return; + } + + mCallbackEnv->SetByteArrayRegion(ps_data, 0, len,(jbyte *)ps); + jbyte* bytes= mCallbackEnv->GetByteArrayElements(ps_data,0); + mCallbackEnv->CallVoidMethod(mCallbacksObj, method_psInfoCallback,ps_data); + mCallbackEnv->DeleteLocalRef(ps_data); +} + +void fm_oda_update_cb() +{ + ALOGE("ODA_EVT"); +} + +void fm_rt_plus_update_cb(char *rt_plus) +{ + jbyteArray RtPlus = NULL; + ALOGE("RT_PLUS"); + int len; + + len = (int)(rt_plus[0] & 0x0F); + ALOGE(" rt plus len=%d :",len); + RtPlus = mCallbackEnv->NewByteArray(len); + if (RtPlus == NULL) { + ALOGE(" rt plus data allocate failed :"); + return; + } + mCallbackEnv->SetByteArrayRegion(RtPlus, 0, len,(jbyte *)rt_plus); + mCallbackEnv->CallVoidMethod(mCallbacksObj, method_rtplusCallback,RtPlus); + mCallbackEnv->DeleteLocalRef(RtPlus); +} + +void fm_ert_update_cb(char *ert) +{ + ALOGE("ERT_EVT"); + jbyteArray ert_buff = NULL; + int i,len; + + if (!checkCallbackThread()) { + ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); + return; + } + + len = (int)(ert[0] & 0x0F); + len = len+3; + + ALOGE(" ert data len=%d :",len); + ert_buff = mCallbackEnv->NewByteArray(len); + if (ert_buff == NULL) { + ALOGE(" ps data allocate failed :"); + return; + } + + mCallbackEnv->SetByteArrayRegion(ert_buff, 0, len,(jbyte *)ert); + jbyte* bytes= mCallbackEnv->GetByteArrayElements(ert_buff,0); + mCallbackEnv->CallVoidMethod(mCallbacksObj, method_ertCallback,ert_buff); + mCallbackEnv->DeleteLocalRef(ert_buff); +} + +void fm_disabled_cb() +{ + ALOGE("DISABLE"); + mCallbackEnv->CallVoidMethod(mCallbacksObj, method_disableCallback); +} -using namespace android; +static void fm_thread_evt_cb(unsigned int event) { + JavaVM* vm = AndroidRuntime::getJavaVM(); + if (event == 0) { + JavaVMAttachArgs args; + char name[] = "FM Service Callback Thread"; + args.version = JNI_VERSION_1_6; + args.name = name; + args.group = NULL; + vm->AttachCurrentThread(&mCallbackEnv, &args); + ALOGE("satish: Callback thread attached: %p", mCallbackEnv); + } else if (event == 1) { + if (!checkCallbackThread()) { + ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); + return; + } + vm->DetachCurrentThread(); + } +} +typedef struct { + size_t size; + + enb_result_cb enabled_cb; + tune_rsp_cb tune_cb; + seek_rsp_cb seek_cmpl_cb; + scan_rsp_cb scan_next_cb; + srch_list_rsp_cb srch_list_cb; + stereo_mode_cb stereo_status_cb; + rds_avl_sts_cb rds_avail_status_cb; + af_list_cb af_list_update_cb; + rt_cb rt_update_cb; + ps_cb ps_update_cb; + oda_cb oda_update_cb; + rt_plus_cb rt_plus_update_cb; + ert_cb ert_update_cb; + disable_cb disabled_cb; + callback_thread_event thread_evt_cb; +} fm_vendor_callbacks_t; + +typedef struct { + int (*hal_init)(fm_vendor_callbacks_t *p_cb); + + int (*set_fm_ctrl)(int ioctl, int val); + int (*get_fm_ctrl) (int ioctl, int val); +} fm_interface_t; + +fm_interface_t *vendor_interface; +static fm_vendor_callbacks_t fm_callbacks = { + sizeof(fm_callbacks), + fm_enabled_cb, + fm_tune_cb, + fm_seek_cmpl_cb, + fm_scan_next_cb, + fm_srch_list_cb, + fm_stereo_status_cb, + fm_rds_avail_status_cb, + fm_af_list_update_cb, + fm_rt_update_cb, + fm_ps_update_cb, + fm_oda_update_cb, + fm_rt_plus_update_cb, + fm_ert_update_cb, + fm_disabled_cb, + fm_thread_evt_cb +}; /* native interface */ static jint android_hardware_fmradio_FmReceiverJNI_acquireFdNative @@ -178,7 +477,8 @@ static jint android_hardware_fmradio_FmReceiverJNI_getFreqNative int err; long freq; - if (fd >= 0) { + err = vendor_interface->get_fm_ctrl(V4L2_CID_PRV_IRIS_FREQ, freq); +/* if (fd >= 0) { err = FmIoctlsInterface :: get_cur_freq(fd, freq); if(err < 0) { err = FM_JNI_FAILURE; @@ -190,7 +490,7 @@ static jint android_hardware_fmradio_FmReceiverJNI_getFreqNative ALOGE("%s: get freq failed because fd is negative, fd: %d\n", LOG_TAG, fd); err = FM_JNI_FAILURE; - } + } */ return err; } @@ -200,7 +500,8 @@ static jint android_hardware_fmradio_FmReceiverJNI_setFreqNative { int err; - if ((fd >= 0) && (freq > 0)) { + err = vendor_interface->set_fm_ctrl(V4L2_CID_PRV_IRIS_FREQ, freq); +/* if ((fd >= 0) && (freq > 0)) { err = FmIoctlsInterface :: set_freq(fd, freq); if (err < 0) { ALOGE("%s: set freq failed, freq: %d\n", LOG_TAG, freq); @@ -212,7 +513,7 @@ static jint android_hardware_fmradio_FmReceiverJNI_setFreqNative ALOGE("%s: set freq failed because either fd/freq is negative,\ fd: %d, freq: %d\n", LOG_TAG, fd, freq); err = FM_JNI_FAILURE; - } + } */ return err; } @@ -223,7 +524,7 @@ static jint android_hardware_fmradio_FmReceiverJNI_setControlNative int err; ALOGE("id(%x) value: %x\n", id, value); - if ((fd >= 0) && (id >= 0)) { +/* if ((fd >= 0) && (id >= 0)) { err = FmIoctlsInterface :: set_control(fd, id, value); if (err < 0) { ALOGE("%s: set control failed, id: %d\n", LOG_TAG, id); @@ -235,7 +536,8 @@ static jint android_hardware_fmradio_FmReceiverJNI_setControlNative ALOGE("%s: set control failed because either fd/id is negavtive,\ fd: %d, id: %d\n", LOG_TAG, fd, id); err = FM_JNI_FAILURE; - } + } */ + err = vendor_interface->set_fm_ctrl(id, value); return err; } @@ -271,7 +573,14 @@ static jint android_hardware_fmradio_FmReceiverJNI_getControlNative ALOGE("id(%x)\n", id); - if ((fd >= 0) && (id >= 0)) { + err = vendor_interface->get_fm_ctrl(id, val); + if (err < 0) { + ALOGE("%s: get control failed, id: %d\n", LOG_TAG, id); + err = FM_JNI_FAILURE; + } else { + err = val; + } +/* if ((fd >= 0) && (id >= 0)) { err = FmIoctlsInterface :: get_control(fd, id, val); if (err < 0) { ALOGE("%s: get control failed, id: %d\n", LOG_TAG, id); @@ -283,7 +592,7 @@ static jint android_hardware_fmradio_FmReceiverJNI_getControlNative ALOGE("%s: get control failed because either fd/id is negavtive,\ fd: %d, id: %d\n", LOG_TAG, fd, id); err = FM_JNI_FAILURE; - } + } */ return err; } @@ -294,7 +603,14 @@ static jint android_hardware_fmradio_FmReceiverJNI_startSearchNative { int err; - if ((fd >= 0) && (dir >= 0)) { + err = vendor_interface->set_fm_ctrl(V4L2_CID_PRV_IRIS_SEEK, dir); + if (err < 0) { + ALOGE("%s: search failed, dir: %d\n", LOG_TAG, dir); + err = FM_JNI_FAILURE; + } else { + err = FM_JNI_SUCCESS; + } +/* if ((fd >= 0) && (dir >= 0)) { ALOGD("startSearchNative: Issuing the VIDIOC_S_HW_FREQ_SEEK"); err = FmIoctlsInterface :: start_search(fd, dir); if (err < 0) { @@ -307,7 +623,7 @@ static jint android_hardware_fmradio_FmReceiverJNI_startSearchNative ALOGE("%s: search failed because either fd/dir is negative,\ fd: %d, dir: %d\n", LOG_TAG, fd, dir); err = FM_JNI_FAILURE; - } + } */ return err; } @@ -318,7 +634,14 @@ static jint android_hardware_fmradio_FmReceiverJNI_cancelSearchNative { int err; - if (fd >= 0) { + err = vendor_interface->set_fm_ctrl(V4L2_CID_PRV_SRCHON, 0); + if (err < 0) { + ALOGE("%s: cancel search failed\n", LOG_TAG); + err = FM_JNI_FAILURE; + } else { + err = FM_JNI_SUCCESS; + } +/* if (fd >= 0) { err = FmIoctlsInterface :: set_control(fd, V4L2_CID_PRV_SRCHON, 0); if (err < 0) { ALOGE("%s: cancel search failed\n", LOG_TAG); @@ -330,7 +653,7 @@ static jint android_hardware_fmradio_FmReceiverJNI_cancelSearchNative ALOGE("%s: cancel search failed because fd is negative, fd: %d\n", LOG_TAG, fd); err = FM_JNI_FAILURE; - } + } */ return err; } @@ -365,7 +688,20 @@ static jint android_hardware_fmradio_FmReceiverJNI_setBandNative { int err; - if ((fd >= 0) && (low >= 0) && (high >= 0)) { + err = vendor_interface->set_fm_ctrl(V4L2_CID_PRV_IRIS_UPPER_BAND, high); + if (err < 0) { + ALOGE("%s: set band failed, high: %d\n", LOG_TAG, high); + err = FM_JNI_FAILURE; + return err; + } + err = vendor_interface->set_fm_ctrl(V4L2_CID_PRV_IRIS_LOWER_BAND, low); + if (err < 0) { + ALOGE("%s: set band failed, low: %d\n", LOG_TAG, low); + err = FM_JNI_FAILURE; + } else { + err = FM_JNI_SUCCESS; + } +/* if ((fd >= 0) && (low >= 0) && (high >= 0)) { err = FmIoctlsInterface :: set_band(fd, low, high); if (err < 0) { ALOGE("%s: set band failed, low: %d, high: %d\n", @@ -378,7 +714,7 @@ static jint android_hardware_fmradio_FmReceiverJNI_setBandNative ALOGE("%s: set band failed because either fd/band is negative,\ fd: %d, low: %d, high: %d\n", LOG_TAG, fd, low, high); err = FM_JNI_FAILURE; - } + } */ return err; } @@ -390,7 +726,14 @@ static jint android_hardware_fmradio_FmReceiverJNI_getLowerBandNative int err; ULINT freq; - if (fd >= 0) { + err = vendor_interface->get_fm_ctrl(V4L2_CID_PRV_IRIS_LOWER_BAND, freq); + if (err < 0) { + ALOGE("%s: get lower band failed\n", LOG_TAG); + err = FM_JNI_FAILURE; + } else { + err = freq; + } +/* if (fd >= 0) { err = FmIoctlsInterface :: get_lowerband_limit(fd, freq); if (err < 0) { ALOGE("%s: get lower band failed\n", LOG_TAG); @@ -402,7 +745,7 @@ static jint android_hardware_fmradio_FmReceiverJNI_getLowerBandNative ALOGE("%s: get lower band failed because fd is negative,\ fd: %d\n", LOG_TAG, fd); err = FM_JNI_FAILURE; - } + } */ return err; } @@ -414,7 +757,14 @@ static jint android_hardware_fmradio_FmReceiverJNI_getUpperBandNative int err; ULINT freq; - if (fd >= 0) { + err = vendor_interface->get_fm_ctrl(V4L2_CID_PRV_IRIS_UPPER_BAND, freq); + if (err < 0) { + ALOGE("%s: get upper band failed\n", LOG_TAG); + err = FM_JNI_FAILURE; + } else { + err = freq; + } +/* if (fd >= 0) { err = FmIoctlsInterface :: get_upperband_limit(fd, freq); if (err < 0) { ALOGE("%s: get lower band failed\n", LOG_TAG); @@ -426,7 +776,7 @@ static jint android_hardware_fmradio_FmReceiverJNI_getUpperBandNative ALOGE("%s: get lower band failed because fd is negative,\ fd: %d\n", LOG_TAG, fd); err = FM_JNI_FAILURE; - } + } */ return err; } @@ -437,7 +787,14 @@ static jint android_hardware_fmradio_FmReceiverJNI_setMonoStereoNative int err; - if (fd >= 0) { + err = vendor_interface->get_fm_ctrl(V4L2_CID_PRV_IRIS_AUDIO_MODE, val); + if (err < 0) { + ALOGE("%s: set audio mode failed\n", LOG_TAG); + err = FM_JNI_FAILURE; + } else { + err = FM_JNI_SUCCESS; + } +/* if (fd >= 0) { err = FmIoctlsInterface :: set_audio_mode(fd, (enum AUDIO_MODE)val); if (err < 0) { err = FM_JNI_FAILURE; @@ -446,7 +803,7 @@ static jint android_hardware_fmradio_FmReceiverJNI_setMonoStereoNative } } else { err = FM_JNI_FAILURE; - } + } */ return err; } @@ -927,11 +1284,83 @@ static jint android_hardware_fmradio_FmReceiverJNI_setSpurDataNative return FM_JNI_SUCCESS; } +static void classInitNative(JNIEnv* env, jclass clazz) { + + ALOGE("ClassInit native called \n"); + jclass dataClass = env->FindClass("qcom/fmradio/FmReceiverJNI"); + javaClassRef = (jclass) env->NewGlobalRef(dataClass); + lib_handle = dlopen(FM_LIBRARY_NAME, RTLD_NOW); + if (!lib_handle) { + ALOGE("%s unable to open %s: %s", __func__, FM_LIBRARY_NAME, dlerror()); + goto error; + } + ALOGE("Opened %s shared object library successfully", FM_LIBRARY_NAME); + + ALOGI("Obtaining handle: '%s' to the shared object library...", FM_LIBRARY_SYMBOL_NAME); + vendor_interface = (fm_interface_t *)dlsym(lib_handle, FM_LIBRARY_SYMBOL_NAME); + if (!vendor_interface) { + ALOGE("%s unable to find symbol %s in %s: %s", __func__, FM_LIBRARY_SYMBOL_NAME, FM_LIBRARY_NAME, dlerror()); + goto error; + } + + method_psInfoCallback = env->GetMethodID(javaClassRef, "PsInfoCallback", "([B)V"); + method_rtCallback = env->GetMethodID(javaClassRef, "RtCallback", "([B)V"); + method_ertCallback = env->GetMethodID(javaClassRef, "ErtCallback", "([B)V"); + method_rtplusCallback = env->GetMethodID(javaClassRef, "RtPlusCallback", "([B)V"); + method_aflistCallback = env->GetMethodID(javaClassRef, "AflistCallback", "([B)V"); + ALOGI("method_psInfoCallback: '%p' env =%p...", method_psInfoCallback, env); + method_enableCallback = env->GetMethodID(javaClassRef, "enableCallback", "()V"); + method_tuneCallback = env->GetMethodID(javaClassRef, "tuneCallback", "(I)V"); + method_seekCmplCallback = env->GetMethodID(javaClassRef, "seekCmplCallback", "(I)V"); + method_scanNxtCallback = env->GetMethodID(javaClassRef, "scanNxtCallback", "()V"); + //method_srchListCallback = env->GetMethodID(javaClassRef, "srchListCallback", "([B)V"); + method_stereostsCallback = env->GetMethodID(javaClassRef, "stereostsCallback", "(Z)V"); + method_rdsAvlStsCallback = env->GetMethodID(javaClassRef, "rdsAvlStsCallback", "(Z)V"); + method_disableCallback = env->GetMethodID(javaClassRef, "disableCallback", "()V"); + + return; +error: + vendor_interface = NULL; + if (lib_handle) + dlclose(lib_handle); + lib_handle = NULL; +} + +static void initNative(JNIEnv *env, jobject object) { + + int status; + ALOGE("Init native called \n"); + + if (vendor_interface) { + ALOGE("Initializing the FM HAL module & registering the JNI callback functions..."); + status = vendor_interface->hal_init(&fm_callbacks); + if (status) { + ALOGE("%s unable to initialize vendor library: %d", __func__, status); + return; + } + ALOGE("***** FM HAL Initialization complete *****\n"); + } + ALOGE("object =%p, env = %p\n",object,env); + mCallbacksObj = env->NewGlobalRef(object); + ALOGE("mCallbackobject =%p, \n",mCallbacksObj); + + +} +static void cleanupNative(JNIEnv *env, jobject object) { + + if (mCallbacksObj != NULL) { + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = NULL; + } +} /* * JNI registration. */ static JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ + { "classInitNative", "()V", (void*)classInitNative}, + { "initNative", "()V", (void*)initNative}, + {"cleanupNative", "()V", (void *) cleanupNative}, { "acquireFdNative", "(Ljava/lang/String;)I", (void*)android_hardware_fmradio_FmReceiverJNI_acquireFdNative}, { "closeFdNative", "(I)I", @@ -996,21 +1425,24 @@ int register_android_hardware_fm_fmradio(JNIEnv* env) { return jniRegisterNativeMethods(env, "qcom/fmradio/FmReceiverJNI", gMethods, NELEM(gMethods)); } +} // end namespace + jint JNI_OnLoad(JavaVM *jvm, void *reserved) { - JNIEnv *e; - int status; - ALOGE("FM : loading QCOMM FM-JNI\n"); - - if(jvm->GetEnv((void **)&e, JNI_VERSION_1_6)) { - ALOGE("JNI version mismatch error"); - return JNI_ERR; - } + JNIEnv *e; + int status; + g_jVM = jvm; + + ALOGE("FM : Loading QCOMM FM-JNI"); + if (jvm->GetEnv((void **)&e, JNI_VERSION_1_6)) { + ALOGE("JNI version mismatch error"); + return JNI_ERR; + } - if ((status = register_android_hardware_fm_fmradio(e)) < 0) { - ALOGE("jni adapter service registration failure, status: %d", status); - return JNI_ERR; - } - return JNI_VERSION_1_6; + if ((status = android::register_android_hardware_fm_fmradio(e)) < 0) { + ALOGE("jni adapter service registration failure, status: %d", status); + return JNI_ERR; + } + return JNI_VERSION_1_6; } diff --git a/qcom/fmradio/FmReceiver.java b/qcom/fmradio/FmReceiver.java index 732c0b2..a52a233 100644 --- a/qcom/fmradio/FmReceiver.java +++ b/qcom/fmradio/FmReceiver.java @@ -30,7 +30,8 @@ package qcom.fmradio; import android.util.Log; import android.os.SystemProperties; - +import java.util.Arrays; +import java.lang.Runnable; /** * This class contains all interfaces and types needed to * Control the FM receiver. @@ -271,7 +272,9 @@ public class FmReceiver extends FmTransceiver private static final int TAVARUA_BUF_AF_LIST=5; private static final int TAVARUA_BUF_MAX=6; - private FmRxEvCallbacksAdaptor mCallback; + public static FmRxEvCallbacksAdaptor mCallback; + static FmRxEvCallbacks callback; + static FmReceiverJNI mFmReceiverJNI; /** * Internal Constants for Signal thresholds * @@ -323,8 +326,12 @@ public class FmReceiver extends FmTransceiver mControl = new FmRxControls(); mRxEvents = new FmRxEventListner(); + Log.e(TAG, "FmReceiver constructor"); //registerClient(callback); mCallback = callback; + if (mCallback == null) + Log.e(TAG, "mCallback is NULL"); + mFmReceiverJNI = new FmReceiverJNI(mCallback); } @@ -454,7 +461,7 @@ public class FmReceiver extends FmTransceiver if( status == true ) { /* Do Receiver Specific Enable Stuff here.*/ - status = registerClient(mCallback); + // status = registerClient(mCallback); mRdsData = new FmRxRdsData(sFd); } else { @@ -1398,16 +1405,20 @@ public class FmReceiver extends FmTransceiver int piLower = 0; int piHigher = 0; - FmReceiverJNI.getBufferNative(sFd, buff, 3); + // FmReceiverJNI.getBufferNative(sFd, buff, 3); + buff = FmReceiverJNI.getPsBuffer(buff); + /* byte is signed ;( * knock down signed bits */ piLower = buff[3] & 0xFF; piHigher = buff[2] & 0xFF; int pi = ((piHigher << 8) | piLower); + Log.d (TAG, "PI= " + pi); mRdsData.setPrgmId (pi); mRdsData.setPrgmType ( (int)( buff[1] & 0x1F)); int numOfPs = (int)(buff[0] & 0x0F); + Log.d (TAG, "numofpsI= " + numOfPs); try { @@ -1448,7 +1459,8 @@ public class FmReceiver extends FmTransceiver int piLower = 0; int piHigher = 0; - FmReceiverJNI.getBufferNative(sFd, buff, 2); + // FmReceiverJNI.getBufferNative(sFd, buff, 2); + buff = FmReceiverJNI.getPsBuffer(buff); String rdsStr = new String(buff); /* byte is signed ;( * knock down signed bit @@ -1478,7 +1490,9 @@ public class FmReceiver extends FmTransceiver int i, j = 2; byte tag_code, tag_len, tag_start_pos; - bytes_read = FmReceiverJNI.getBufferNative(sFd, rt_plus, BUF_RTPLUS); +// bytes_read = FmReceiverJNI.getBufferNative(sFd, rt_plus, BUF_RTPLUS); + rt_plus = FmReceiverJNI.getPsBuffer(rt_plus); + bytes_read = rt_plus[0]; if (bytes_read > 0) { if (rt_plus[RT_OR_ERT_IND] == 0) rt = mRdsData.getRadioText(); @@ -1514,7 +1528,9 @@ public class FmReceiver extends FmTransceiver String encoding_type = "UCS-2"; int bytes_read; - bytes_read = FmReceiverJNI.getBufferNative(sFd, raw_ert, BUF_ERT); + // bytes_read = FmReceiverJNI.getBufferNative(sFd, raw_ert, BUF_ERT); + raw_ert = FmReceiverJNI.getPsBuffer(raw_ert); + bytes_read = raw_ert[0]; if (bytes_read > 0) { ert_text = new byte[raw_ert[LEN_IND]]; for(i = 3; (i - 3) < raw_ert[LEN_IND]; i++) { @@ -1581,7 +1597,8 @@ public class FmReceiver extends FmTransceiver int lowerBand, i; int tunedFreq, PI, size_AFLIST; - FmReceiverJNI.getBufferNative(sFd, buff, TAVARUA_BUF_AF_LIST); + // FmReceiverJNI.getBufferNative(sFd, buff, TAVARUA_BUF_AF_LIST); + buff = FmReceiverJNI.getPsBuffer(buff); if (IsSmdTransportLayer() || IsRomeChip()) { Log.d(TAG, "SMD transport layer or Rome chip"); diff --git a/qcom/fmradio/FmReceiverJNI.java b/qcom/fmradio/FmReceiverJNI.java index c6ca964..8a2be0f 100644 --- a/qcom/fmradio/FmReceiverJNI.java +++ b/qcom/fmradio/FmReceiverJNI.java @@ -27,6 +27,10 @@ */ package qcom.fmradio; +import android.util.Log; +import java.util.Arrays; +import java.lang.Runnable; +import qcom.fmradio.FmReceiver; class FmReceiverJNI { /** @@ -44,8 +48,175 @@ class FmReceiverJNI { * @return The file descriptor of the device * */ - static native int acquireFdNative(String path); + private static final String TAG = "FmReceiverJNI"; + + static { + Log.e(TAG, "classinit native called"); + classInitNative(); + } + static native void classInitNative(); + static native void initNative(); + static native void cleanupNative(); + + final private FmRxEvCallbacks mCallback; + static private final int STD_BUF_SIZE = 256; + static private byte[] mRdsBuffer = new byte[STD_BUF_SIZE]; + + public static byte[] getPsBuffer(byte[] buff) { + Log.e(TAG, "getPsBuffer enter"); + buff = Arrays.copyOf(mRdsBuffer, mRdsBuffer.length); + Log.e(TAG, "getPsBuffer exit"); + return buff; + } + + public void AflistCallback(byte[] aflist) { + Log.e(TAG, "AflistCallback enter " ); + if (aflist == null) { + Log.e(TAG, "aflist null return "); + return; + } + mRdsBuffer = Arrays.copyOf(aflist, aflist.length); + FmReceiver.mCallback.FmRxEvRdsAfInfo(); + Log.e(TAG, "AflistCallback exit " ); + } + + public void RtPlusCallback(byte[] rtplus) { + Log.e(TAG, "RtPlusCallback enter " ); + if (rtplus == null) { + Log.e(TAG, "psInfo null return "); + return; + } + mRdsBuffer = Arrays.copyOf(rtplus, rtplus.length); + FmReceiver.mCallback.FmRxEvRTPlus(); + Log.e(TAG, "RtPlusCallback exit " ); + } + + public void RtCallback(byte[] rt) { + Log.e(TAG, "RtCallback enter " ); + if (rt == null) { + Log.e(TAG, "psInfo null return "); + return; + } + mRdsBuffer = Arrays.copyOf(rt, rt.length); + FmReceiver.mCallback.FmRxEvRdsRtInfo(); + Log.e(TAG, "RtCallback exit " ); + } + + public void ErtCallback(byte[] ert) { + Log.e(TAG, "ErtCallback enter " ); + if (ert == null) { + Log.e(TAG, "ERT null return "); + return; + } + mRdsBuffer = Arrays.copyOf(ert, ert.length); + FmReceiver.mCallback.FmRxEvERTInfo(); + Log.e(TAG, "RtCallback exit " ); + } + + public void PsInfoCallback(byte[] psInfo) { + Log.e(TAG, "PsInfoCallback enter " ); + if (psInfo == null) { + Log.e(TAG, "psInfo null return "); + return; + } + Log.e(TAG, "length = " +psInfo.length); + mRdsBuffer = Arrays.copyOf(psInfo, psInfo.length); + FmReceiver.mCallback.FmRxEvRdsPsInfo(); + Log.e(TAG, "PsInfoCallback exit"); + } + + public void enableCallback() { + Log.e(TAG, "enableCallback enter"); + FmTransceiver.setFMPowerState(FmTransceiver.FMState_Rx_Turned_On); + Log.v(TAG, "RxEvtList: CURRENT-STATE : FMRxStarting ---> NEW-STATE : FMRxOn"); + FmReceiver.mCallback.FmRxEvEnableReceiver(); + Log.e(TAG, "enableCallback exit"); + } + public void tuneCallback(int freq) { + int state; + + Log.e(TAG, "tuneCallback enter"); + state = FmReceiver.getSearchState(); + switch(state) { + case FmTransceiver.subSrchLevel_SrchAbort: + Log.v(TAG, "Current state is SRCH_ABORTED"); + Log.v(TAG, "Aborting on-going search command..."); + /* intentional fall through */ + case FmTransceiver.subSrchLevel_SeekInPrg : + Log.v(TAG, "Current state is " + state); + FmReceiver.setSearchState(FmTransceiver.subSrchLevel_SrchComplete); + Log.v(TAG, "RxEvtList: CURRENT-STATE : Search ---> NEW-STATE : FMRxOn"); + FmReceiver.mCallback.FmRxEvSearchComplete(freq); + break; + default: + if (freq > 0) + FmReceiver.mCallback.FmRxEvRadioTuneStatus(freq); + else + Log.e(TAG, "get frequency command failed"); + break; + } + Log.e(TAG, "tuneCallback exit"); + } + + public void seekCmplCallback(int freq) { + int state; + + Log.e(TAG, "seekCmplCallback enter"); + state = FmReceiver.getSearchState(); + switch(state) { + case FmTransceiver.subSrchLevel_ScanInProg: + Log.v(TAG, "Current state is " + state); + FmReceiver.setSearchState(FmTransceiver.subSrchLevel_SrchComplete); + Log.v(TAG, "RxEvtList: CURRENT-STATE : Search ---> NEW-STATE :FMRxOn"); + FmReceiver.mCallback.FmRxEvSearchComplete(freq); + break; + case FmTransceiver.subSrchLevel_SrchAbort: + Log.v(TAG, "Current state is SRCH_ABORTED"); + Log.v(TAG, "Aborting on-going search command..."); + FmReceiver.setSearchState(FmTransceiver.subSrchLevel_SrchComplete); + Log.v(TAG, "RxEvtList: CURRENT-STATE : Search ---> NEW-STATE : FMRxOn"); + FmReceiver.mCallback.FmRxEvSearchComplete(freq); + break; + } + Log.e(TAG, "seekCmplCallback exit"); + } + + public void scanNxtCallback() { + Log.e(TAG, "scanNxtCallback enter"); + FmReceiver.mCallback.FmRxEvSearchInProgress(); + Log.e(TAG, "scanNxtCallback exit"); + } + + public void stereostsCallback(boolean stereo) { + Log.e(TAG, "stereostsCallback enter"); + FmReceiver.mCallback.FmRxEvStereoStatus (stereo); + Log.e(TAG, "stereostsCallback exit"); + } + + public void rdsAvlStsCallback(boolean rdsAvl) { + Log.e(TAG, "rdsAvlStsCallback enter"); + FmReceiver.mCallback.FmRxEvRdsLockStatus(rdsAvl); + Log.e(TAG, "rdsAvlStsCallback exit"); + } + + public void disableCallback() { + Log.e(TAG, "disableCallback enter"); + FmTransceiver.setFMPowerState(FmTransceiver.FMState_Turned_Off); + Log.v(TAG, "RxEvtList: CURRENT-STATE : FMTurningOff ---> NEW-STATE : FMOff"); + FmReceiver.mCallback.FmRxEvDisableReceiver(); + Log.e(TAG, "disableCallback exit"); + } + + public FmReceiverJNI(FmRxEvCallbacks callback) { + mCallback = callback; + if (mCallback == null) + Log.e(TAG, "mCallback is null in JNI"); + Log.e(TAG, "satish init native called"); + initNative(); + } + + static native int acquireFdNative(String path); /** * native method: diff --git a/qcom/fmradio/FmTransceiver.java b/qcom/fmradio/FmTransceiver.java index b39fd7a..a567152 100644 --- a/qcom/fmradio/FmTransceiver.java +++ b/qcom/fmradio/FmTransceiver.java @@ -405,9 +405,9 @@ public class FmTransceiver boolean status; int ret; //Acquire the deviceon Enable - if( !acquire("/dev/radio0")){ - return false; - } +// if( !acquire("/dev/radio0")){ +// return false; +// } if (new File("/etc/fm/SpurTableFile.txt").isFile()) { Log.d(TAG, "Send Spur roation table"); FmConfig.fmSpurConfig(sFd); -- GitLab