summaryrefslogtreecommitdiffstats
path: root/helium
diff options
context:
space:
mode:
authorSatish kumar sugasi <ssugas@codeaurora.org>2016-05-04 20:15:31 -0700
committerSatish kumar sugasi <ssugas@codeaurora.org>2016-05-12 22:30:32 -0700
commit644b4888d44d3dbdd9861235cb432ebb3b11899c (patch)
treed80204b9f6c88ca053cf3f33beabc175cb6ac521 /helium
parentcfc9c53737e969aa3396cc9f8eb6dd5a7969a30d (diff)
downloadandroid_hardware_qcom_fm-644b4888d44d3dbdd9861235cb432ebb3b11899c.tar.gz
android_hardware_qcom_fm-644b4888d44d3dbdd9861235cb432ebb3b11899c.tar.bz2
android_hardware_qcom_fm-644b4888d44d3dbdd9861235cb432ebb3b11899c.zip
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
Diffstat (limited to 'helium')
-rw-r--r--helium/Android.mk29
-rw-r--r--helium/radio-helium-commands.h99
-rw-r--r--helium/radio-helium.h1137
-rw-r--r--helium/radio_helium_hal.c1067
-rw-r--r--helium/radio_helium_hal_cmds.c256
5 files changed, 2588 insertions, 0 deletions
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);
+}
+