libfmjni: Implement hal layer

Implement HAL layer that can be plugged in
with Android FM APP to get fm functionalities.

Change-Id: Id16d37a320fdbacb505ba7ecc431622db17bd4d7
diff --git a/Android.mk b/Android.mk
index 39d9305..027bddf 100644
--- a/Android.mk
+++ b/Android.mk
@@ -20,6 +20,8 @@
 include $(LOCAL_PATH)/fmapp2/Android.mk
 LOCAL_PATH := $(LOCAL_DIR_PATH)
 include $(LOCAL_PATH)/FMRecord/Android.mk
+LOCAL_PATH := $(LOCAL_DIR_PATH)
+include $(LOCAL_PATH)/libfm_jni/Android.mk
 endif # is-vendor-board-platform
 endif # BOARD_HAVE_QCOM_FM
 
diff --git a/libfm_jni/Android.mk b/libfm_jni/Android.mk
new file mode 100644
index 0000000..28f770d
--- /dev/null
+++ b/libfm_jni/Android.mk
@@ -0,0 +1,29 @@
+ifneq (,$(filter $(QCOM_BOARD_PLATFORMS),$(TARGET_BOARD_PLATFORM)))
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    FmIoctlsInterface.cpp \
+    ConfigFmThs.cpp \
+    FmPerformanceParams.cpp \
+    ConfFileParser.cpp \
+    FmRadioController.cpp \
+    LibfmJni.cpp
+
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) \
+    frameworks/base/include/media
+
+LOCAL_SHARED_LIBRARIES := \
+    libcutils \
+    libdl \
+    libmedia \
+    libnativehelper \
+
+#LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include
+#LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr
+
+LOCAL_MODULE := libfmjni
+include $(BUILD_SHARED_LIBRARY)
+
+endif
diff --git a/libfm_jni/ConfFileParser.cpp b/libfm_jni/ConfFileParser.cpp
new file mode 100644
index 0000000..f3d32a4
--- /dev/null
+++ b/libfm_jni/ConfFileParser.cpp
@@ -0,0 +1,918 @@
+/*
+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 <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <math.h>
+#include <utils/Log.h>
+#include "ConfFileParser.h"
+
+//declaration of functions only specific to this file
+static char parse_line
+(
+  group_table *key_file,
+  const char *line,
+  char **cur_grp
+);
+
+static char parse_load_frm_fhandler
+(
+  group_table *key_file,
+  FILE *fp
+);
+
+static char line_is_grp
+(
+  group_table *key_file,
+  const char *str,
+  char **cur_grp
+);
+
+static void free_grp_list
+(
+  group *a
+);
+
+static void free_key_list
+(
+  key_value_pair_list *a
+);
+
+static char line_is_key_value_pair
+(
+  group_table *key_file,
+  const char *str,
+  const char *cur_grp
+);
+
+static char line_is_comment
+(
+  const char *str
+);
+
+static char grp_exist
+(
+  const group_table *key_file,
+  const char *new_grp
+);
+
+static char add_grp
+(
+  group_table *key_file,
+  const char *new_grp
+);
+
+static group *alloc_group
+(
+  void
+);
+
+static key_value_pair_list *alloc_key_value_pair
+(
+  void
+);
+
+static char add_key_value_pair
+(
+  group_table *key_file,
+  const char *cur_grp,
+  const char *key,
+  const char *val
+);
+
+
+//Definitions
+void free_strs
+(
+  char **str_array
+)
+{
+  char **str_array_cpy = str_array;
+  if(str_array != NULL) {
+     while(*str_array != NULL) {
+           free(*str_array);
+           str_array++;
+     }
+  }
+  free(str_array_cpy);
+}
+//ToDo: Come up with code hashing
+//function
+unsigned int get_hash_code
+(
+  const char *str
+)
+{
+
+  unsigned len = strlen(str);
+  unsigned int i;
+  unsigned int hash_code = 0;
+
+  for(i = 0; len > 0; len--, i++) {
+      hash_code += (int)((str[i] * pow(2, len))) % INT_MAX;
+      hash_code %= INT_MAX;
+  }
+  return hash_code;
+}
+
+static key_value_pair_list *alloc_key_value_pair
+(
+  void
+)
+{
+  key_value_pair_list *key_list = NULL;
+
+  key_list = (key_value_pair_list *)malloc(
+                                       sizeof(key_value_pair_list));
+  if(key_list != NULL) {
+     key_list->key = NULL;
+     key_list->next = NULL;
+     key_list->value = NULL;
+  }
+  return key_list;
+}
+
+static group * alloc_group
+(
+  void
+)
+{
+  group *grp = NULL;
+  unsigned int i;
+
+  grp = (group *)malloc(sizeof(group));
+  if(grp != NULL) {
+     grp->grp_name = NULL;
+     grp->grp_next = NULL;
+     grp->num_of_keys = 0;
+     grp->keys_hash_size = MAX_UNIQ_KEYS;
+     grp->list = (key_value_pair_list **)malloc
+                    (sizeof(key_value_pair_list *) * grp->keys_hash_size);
+     if(grp->list == NULL) {
+        ALOGE("Could not alloc group\n");
+        free(grp);
+        grp = NULL;
+     }else {
+        for(i = 0; i < grp->keys_hash_size; i++) {
+            grp->list[i] = NULL;
+        }
+     }
+  }
+  return grp;
+}
+
+group_table *get_key_file
+(
+)
+{
+  group_table *t = NULL;
+  unsigned int i;
+
+  t = (group_table *)malloc(sizeof(group_table));
+  if (t != NULL) {
+      t->grps_hash_size = MAX_UNIQ_GRPS;
+      t->num_of_grps = 0;
+      t->grps_hash = (group **)malloc(sizeof(group *)
+                                       * t->grps_hash_size);
+      if (t->grps_hash == NULL) {
+          free(t);
+          return NULL;
+      }
+      for(i = 0; i < t->grps_hash_size; i++) {
+          t->grps_hash[i] = NULL;
+      }
+  }
+  return t;
+}
+
+void free_key_file(
+  group_table *key_file
+)
+{
+  unsigned int i;
+
+  if(key_file != NULL) {
+     if(key_file->grps_hash != NULL) {
+        for(i = 0; i < key_file->grps_hash_size; i++) {
+            free_grp_list(key_file->grps_hash[i]);
+        }
+     }
+     free(key_file->grps_hash);
+     free(key_file);
+  }
+}
+
+static void free_grp_list
+(
+  group *a
+)
+{
+  group *next;
+  unsigned int i;
+
+  while(a != NULL) {
+       next = a->grp_next;
+       if(a->list != NULL) {
+          for(i = 0; i < a->keys_hash_size; i++) {
+              free_key_list(a->list[i]);
+          }
+       }
+       free(a->grp_name);
+       free(a->list);
+       free(a);
+       a = next;
+  }
+}
+
+static void free_key_list
+(
+  key_value_pair_list *a
+)
+{
+  key_value_pair_list *next;
+
+  while(a != NULL) {
+       next = a->next;
+       free(a->key);
+       free(a->value);
+       free(a);
+       a = next;
+  }
+}
+//return all the groups
+//present in the file
+char **get_grps
+(
+  const group_table *key_file
+)
+{
+  char **grps = NULL;
+  unsigned int i = 0;
+  unsigned int j = 0;
+  unsigned int grp_len;
+  group *grp_list;
+
+  if((key_file == NULL)
+     || (key_file->grps_hash == NULL)
+     || (key_file->grps_hash_size == 0)
+     || (key_file->num_of_grps == 0)) {
+     return grps;
+  }
+  grps = (char **)calloc((key_file->num_of_grps + 1),
+                           sizeof(char *));
+  if(grps == NULL) {
+     return grps;
+  }
+  for(i = 0; i < key_file->grps_hash_size; i++) {
+      grp_list = key_file->grps_hash[i];
+      while(grp_list != NULL) {
+            grp_len = strlen(grp_list->grp_name);
+            grps[j] = (char *)malloc(sizeof(char) *
+                                     (grp_len + 1));
+            if(grps[j] == NULL) {
+               free_strs(grps);
+               grps = NULL;
+               return grps;
+            }
+            memcpy(grps[j], grp_list->grp_name,
+                   (grp_len + 1));
+            grp_list = grp_list->grp_next;
+            j++;
+      }
+  }
+  grps[j] = NULL;
+  return grps;
+}
+
+//returns the list of keys
+//associated with group name
+char **get_keys
+(
+  const group_table *key_file,
+  const char *grp_name
+)
+{
+  unsigned int grp_hash_code;
+  unsigned int grp_index;
+  unsigned int num_of_keys;
+  unsigned int i;
+  unsigned int j = 0;
+  unsigned int key_len;
+  group *grp;
+  key_value_pair_list *key_val_list;
+  char **keys = NULL;
+
+  if((key_file == NULL) || (grp_name == NULL)
+     || (key_file->num_of_grps == 0) ||
+     (key_file->grps_hash_size == 0) ||
+     (key_file->grps_hash == NULL) ||
+     (!strcmp(grp_name, ""))) {
+      return keys;
+  }
+  grp_hash_code = get_hash_code(grp_name);
+  grp_index = (grp_hash_code % key_file->grps_hash_size);
+  grp = key_file->grps_hash[grp_index];
+  while(grp != NULL) {
+        if(!strcmp(grp_name, grp->grp_name)) {
+            if((grp->num_of_keys == 0)
+               || (grp->keys_hash_size == 0)
+               || (grp->list == 0)) {
+               return keys;
+            }
+            keys = (char **)calloc((grp->num_of_keys + 1),
+                                   sizeof(char *));
+            if(keys == NULL) {
+                return keys;
+            }
+            for(i = 0; i < grp->keys_hash_size; i++) {
+                key_val_list = grp->list[i];
+                while(key_val_list != NULL) {
+                     key_len = strlen(key_val_list->key);
+                     keys[j] = (char *)malloc(sizeof(char) *
+                                              (key_len + 1));
+                     if(keys[j] == NULL) {
+                         free_strs(keys);
+                         keys = NULL;
+                         return keys;
+                     }
+                     memcpy(keys[j], key_val_list->key,
+                            (key_len + 1));
+                     j++;
+                     key_val_list = key_val_list->next;
+                }
+            }
+            keys[j] = NULL;
+            return keys;
+        }
+        grp = grp->grp_next;
+  }
+  return keys;
+}
+
+char *get_value
+(
+   const group_table *key_file,
+   const char *grp_name,
+   const char *key
+)
+{
+   unsigned int grp_hash_code;
+   unsigned int key_hash_code;
+   unsigned int grp_index;
+   unsigned int key_index;
+   unsigned val_len;
+   char *val = NULL;
+   group *grp;
+   key_value_pair_list *list;
+
+   if((key_file == NULL) || (grp_name == NULL)
+      || (key == NULL) || (key_file->grps_hash == NULL)
+      || (key_file->grps_hash_size == 0) || !strcmp(grp_name, "")
+      ||(!strcmp(key, ""))) {
+       return NULL;
+   }
+   grp_hash_code = get_hash_code(grp_name);
+   key_hash_code = get_hash_code(key);
+   grp_index = (grp_hash_code % key_file->grps_hash_size);
+   grp = key_file->grps_hash[grp_index];
+   while(grp != NULL) {
+         if(!strcmp(grp_name, grp->grp_name) && grp->keys_hash_size
+            && grp->list) {
+            key_index = (key_hash_code % grp->keys_hash_size);
+            list = grp->list[key_index];
+            while((list != NULL) && (strcmp(list->key, key))) {
+                   list = list->next;
+            }
+            if(list != NULL) {
+                val_len = strlen(list->value);
+                val = (char *)malloc(sizeof(char) * (val_len + 1));
+                if(val != NULL) {
+                   memcpy(val, list->value, val_len);
+                   val[val_len] = '\0';
+                }
+            }
+            return val;
+         }
+         grp = grp->grp_next;
+   }
+   return val;
+}
+//open the file,
+//read, parse and load
+//returns PARSE_SUCCESS if successfully
+//loaded else PARSE_FAILED
+char parse_load_file
+(
+  group_table *key_file,
+  const char *file
+)
+{
+  FILE *fp;
+  char ret = FALSE;
+
+  if((file == NULL) || !strcmp(file, "")) {
+     ALOGE("File name is null or empty \n");
+     return ret;
+  }
+
+  fp = fopen(file, "r");
+  if(fp == NULL) {
+     ALOGE("could not open file for read\n");
+     return ret;
+  }
+
+  ret = parse_load_frm_fhandler(key_file, fp);
+  fclose(fp);
+
+  return ret;
+}
+
+//Read block of data from file handler
+//extract each line, check kind of line(comment,
+//group, key value pair)
+static char parse_load_frm_fhandler
+(
+  group_table *key_file,
+  FILE *fp
+)
+{
+  char buf[MAX_LINE_LEN];
+  char ret = TRUE;
+  char *line = NULL;
+  void *new_line;
+  char *cur_grp = NULL;
+  unsigned line_len = 0;
+  unsigned line_allocated = 0;
+  unsigned int bytes_read = 0;
+  unsigned int i;
+  bool has_carriage_rtn = false;
+
+  while((bytes_read = fread(buf, 1, MAX_LINE_LEN, fp))) {
+        for(i = 0; i < bytes_read; i++) {
+            if(line_len == line_allocated) {
+                line_allocated += 25;
+                new_line = realloc(line, line_allocated);
+                if(new_line == NULL) {
+                   ret = FALSE;
+                   ALOGE("memory allocation failed for line\n");
+                   break;
+                }
+                line = (char *)new_line;
+            }
+            if((buf[i] == '\n')) {
+                has_carriage_rtn = false;
+                line[line_len] = '\0';
+                ret = parse_line(key_file, line, &cur_grp);
+                line_len = 0;
+                if(ret == FALSE) {
+                   ALOGE("could not parse the line, line not proper\n");
+                   break;
+                }
+            }else if(buf[i] == '\r') {
+                ALOGE("File has carriage return\n");
+                has_carriage_rtn = true;
+            }else if(has_carriage_rtn) {
+                ALOGE("File format is not proper, no line character\
+                        after carraige return\n");
+                ret = FALSE;
+                break;
+            }else {
+                line[line_len] = buf[i];
+                line_len++;
+            }
+        }
+        if (!ret) {
+            break;
+        }
+  }
+  free(line);
+  free(cur_grp);
+
+  return ret;
+}
+
+//checks whether a line is
+//comment or grp or key pair value
+//and accordingly adds to list
+static char parse_line
+(
+  group_table *key_file,
+  const char *line,
+  char **cur_grp
+)
+{
+  const char *line_begin;
+  char *grp_name;
+  unsigned int len;
+
+  if((line == NULL) || (key_file == NULL)) {
+      ALOGE("key file or line is null\n");
+      return FALSE;
+  }
+
+  for(line_begin = line; isspace(*line_begin);
+          line_begin++);
+
+  if(line_is_comment(line_begin)) {
+      ALOGE("line is comment\n");
+      return TRUE;
+  }else if(line_is_grp(key_file, line_begin, cur_grp)) {
+      ALOGE("line is grp\n");
+      return TRUE;
+  }else if(line_is_key_value_pair(key_file, line_begin, *cur_grp)) {
+      ALOGE("line is key value pair\n");
+      return TRUE;
+  }else {
+     ALOGE("line is neither comment, grp nor key value pair\n");
+     return FALSE;
+  }
+}
+
+static char line_is_comment
+(
+  const char *str
+)
+{
+  if(str == NULL) {
+      return FALSE;
+  }else if(((*str) == '#') || ((*str) == '\0')
+       || ((*str) == '\n')) {
+      return TRUE;
+  }else {
+      ALOGE("line is not comment\n");
+      return FALSE;
+  }
+}
+
+//return true if a group
+//name already exist
+//else false
+static char grp_exist
+(
+  const group_table *key_file,
+  const char *new_grp
+)
+{
+  unsigned hash_code;
+  unsigned int index;
+  group *grp;
+
+  if((key_file == NULL) || (new_grp == NULL)
+     || (!key_file->grps_hash_size)) {
+     return FALSE;
+  }else {
+    hash_code = get_hash_code(new_grp);
+    index = hash_code % key_file->grps_hash_size;
+    grp = key_file->grps_hash[index];
+    while(grp != NULL) {
+          if (!strcmp(grp->grp_name, new_grp))
+              return TRUE;
+          grp = grp->grp_next;
+    }
+    return FALSE;
+  }
+}
+
+//Add a group to group
+//table if it does not exist
+static char add_grp
+(
+  group_table *key_file,
+  const char *new_grp
+)
+{
+  unsigned int hash_code;
+  unsigned int index;
+  unsigned int grp_name_len;
+  group *grp;
+
+  if(!grp_exist(key_file, new_grp)) {
+      if((key_file == NULL) || (new_grp == NULL)
+         || !key_file->grps_hash_size) {
+         return FALSE;
+      }
+      hash_code = get_hash_code(new_grp);
+      ALOGE("group hash code is: %u\n", hash_code);
+      index = hash_code % key_file->grps_hash_size;
+      ALOGE("group index is: %u\n", index);
+      grp = alloc_group();
+      if(grp == NULL) {
+         return FALSE;
+      }
+      grp_name_len = strlen(new_grp);
+      grp->grp_name = (char *)malloc(
+                                  sizeof(char) * (grp_name_len + 1));
+      if(grp->grp_name == NULL) {
+         ALOGE("could not alloc memory for group name\n");
+         ALOGE("Add group failed\n");
+         free_grp_list(grp);
+         return FALSE;
+      }else {
+         memcpy(grp->grp_name, new_grp, (grp_name_len + 1));
+      }
+      grp->grp_next = key_file->grps_hash[index];
+      key_file->grps_hash[index] = grp;
+      key_file->num_of_grps++;
+      return TRUE;
+  }else {
+     return FALSE;
+  }
+}
+
+//checks validity of a group
+//a valid group is
+//inside [] group name must be
+//alphanumeric
+//Example: [grpName]
+static char line_is_grp
+(
+  group_table *key_file,
+  const char *str,
+  char **cur_grp
+)
+{
+  const char *g_start;
+  const char *g_end;
+  char *new_grp;
+  unsigned int grp_len;
+
+  if ((str == NULL) || (key_file == NULL)) {
+      ALOGE("str is null or key file is null\n");
+      return FALSE;
+  }
+  //checks start mark char ']'
+  if(((*str) != '[')) {
+      ALOGE("start mark is not '['\n");
+      return FALSE;
+  }else {
+      str++;
+      g_start = str;
+  }
+  //checks the end char '['
+  while((*str != '\0') && ((*str) != ']')) {
+        str++;
+  }
+  //if end mark group not found
+  if ((*str) != ']') {
+       ALOGE("grp end mark is not '['\n");
+       return FALSE;
+  }else {
+       g_end = (str - 1);
+  }
+
+  str++;
+  //if end mark found checks the rest chars as well
+  //rest chars should be space
+  while(((*str) == ' ') || ((*str) == '\t')) {
+        str++;
+  }
+  if(*str) {
+     ALOGE("after ']' there are some character\n");
+     return FALSE;
+  }
+
+  str = g_start;
+  while((*g_start != '\0') && (g_start != g_end)
+         && isalnum(*g_start)) {
+        g_start++;
+  }
+  if((g_start == g_end) && isalnum(*g_start)) {
+      //look up if already exist
+      //return false else insert the grp in grp table
+      grp_len = (g_end - str + 1);
+      new_grp = (char *)malloc(sizeof(char) * (grp_len + 1));
+      if (new_grp == NULL) {
+          ALOGE("could not alloc memory for new group\n");
+          return FALSE;
+      }
+      memcpy(new_grp, str, grp_len);
+      new_grp[grp_len] = '\0';
+
+      if(add_grp(key_file, new_grp)) {
+          free(*cur_grp);
+         *cur_grp = new_grp;
+         return TRUE;
+      }else {
+         ALOGE("could not add group to group table\n");
+         return FALSE;
+      }
+  }else {
+      return FALSE;
+  }
+}
+
+static char key_exist
+(
+  const group_table *key_file,
+  const char *cur_grp,
+  const char *key
+)
+{
+  unsigned int grp_hash_code;
+  unsigned int key_hash_code;
+  unsigned int grp_index;
+  unsigned int key_index;
+  group *grp = NULL;
+  key_value_pair_list *list = NULL;
+
+  if((key_file != NULL) && (cur_grp != NULL)
+      && (key != NULL) && ((key_file->grps_hash != NULL))
+      && (strcmp(key, ""))) {
+     grp_hash_code = get_hash_code(cur_grp);
+     grp_index = (grp_hash_code % key_file->grps_hash_size);
+     grp = key_file->grps_hash[grp_index];
+     key_hash_code = get_hash_code(key);
+     while((grp != NULL)) {
+           if(!strcmp(cur_grp, grp->grp_name)) {
+              key_index = (key_hash_code % grp->keys_hash_size);
+              if(grp->list)
+                 list = grp->list[key_index];
+              while((list != NULL) && strcmp(key, list->key)) {
+                    list = list->next;
+              }
+              if(list != NULL){
+                  return TRUE;
+              }else{
+                  return FALSE;
+              }
+           }
+           grp = grp->grp_next;
+     }
+     if(!grp) {
+        return TRUE;
+     }else {
+        return FALSE;
+     }
+  }else {
+     return FALSE;
+  }
+}
+
+//checks validity of key
+//a valid key must start in
+//a seperate line and key must
+//be alphanumeric and before '='
+//there must not be any space
+//Example: key=value
+static char line_is_key_value_pair
+(
+  group_table *key_file,
+  const char *str,
+  const char *cur_grp
+)
+{
+  char *equal_start;
+  char *key;
+  char *val;
+  unsigned key_len;
+  unsigned val_len;
+
+  if((str == NULL) || (cur_grp == NULL) ||
+     !strcmp(cur_grp, "") || (key_file == NULL)) {
+     ALOGE("line is null or cur group or key file is null or empty\n");
+     return FALSE;
+  }
+  equal_start = strchr(str, '=');
+  key_len = (equal_start - str);
+  if((equal_start == NULL) || (equal_start == str)) {
+     ALOGE("line does not have '=' character or no key\n");
+     return FALSE;
+  }
+  while((str != equal_start) && isalnum(*str))
+        str++;
+  if((str == equal_start)) {
+      key = (char *)malloc(sizeof(char) * (key_len + 1));
+      if(key == NULL) {
+         ALOGE("could not alloc memory for new key\n");
+         return FALSE;
+      }
+      equal_start++;
+      val_len = strlen(equal_start);
+      val = (char *)malloc(sizeof(char) * (val_len + 1));
+      if(val == NULL) {
+         ALOGE("could not alloc memory for value\n");
+         return FALSE;
+      }
+      memcpy(key, (str - key_len), key_len);
+      memcpy(val, equal_start, val_len);
+      key[key_len] = '\0';
+      val[val_len] = '\0';
+      ALOGE("Grp: %s, key: %s, value: %s\n", cur_grp, key, val);
+      return add_key_value_pair(key_file,
+                                 cur_grp, key, val);
+  }else {
+     ALOGE("key name doesnot have alpha numeric char\n");
+     return FALSE;
+  }
+}
+
+static char add_key_value_pair
+(
+  group_table *key_file,
+  const char *cur_grp,
+  const char *key,
+  const char *val
+)
+{
+  unsigned int grp_hash_code;
+  unsigned int key_hash_code;
+  unsigned int grp_index;
+  unsigned int key_index;
+  unsigned key_len, val_len;
+  group *grp = NULL;
+  key_value_pair_list *list = NULL;
+
+  if((key_file != NULL) && (cur_grp != NULL)
+      && (key != NULL) && ((key_file->grps_hash != NULL))
+      && (strcmp(key, ""))) {
+     grp_hash_code = get_hash_code(cur_grp);
+     ALOGE("grp hash code is %u\n", grp_hash_code);
+     grp_index = (grp_hash_code % key_file->grps_hash_size);
+     ALOGE("grp index is %u\n", grp_index);
+     grp = key_file->grps_hash[grp_index];
+     key_hash_code = get_hash_code(key);
+     while((grp != NULL)) {
+           if(!strcmp(cur_grp, grp->grp_name)) {
+              key_index = (key_hash_code % grp->keys_hash_size);
+              if(grp->list) {
+                 list = grp->list[key_index];
+              }else {
+                 ALOGE("group list is null\n");
+                 return FALSE;
+              }
+              while((list != NULL) && strcmp(key, list->key)) {
+                    list = list->next;
+              }
+              if(list != NULL) {
+                  ALOGE("group already contains the key\n");
+                  return FALSE;
+              }else{
+                  list = alloc_key_value_pair();
+                  if(list == NULL) {
+                     ALOGE("add key value failed as could not alloc memory for key\
+                            val pair\n");
+                     return FALSE;
+                  }
+                  key_len = strlen(key);
+                  list->key = (char *)malloc(sizeof(char) *
+                                              (key_len + 1));
+                  if(list->key == NULL) {
+                     ALOGE("could not alloc memory for key\n");
+                     free(list);
+                     return FALSE;
+                  }
+                  val_len = strlen(val);
+                  list->value = (char *)malloc(sizeof(char) *
+                                                (val_len + 1));
+                  if(!list->value) {
+                      free(list->key);
+                      free(list);
+                      return FALSE;
+                  }
+                  memcpy(list->key, key, key_len);
+                  memcpy(list->value, val, val_len);
+                  list->key[key_len] = '\0';
+                  list->value[val_len] = '\0';
+                  list->next = grp->list[key_index];
+                  grp->list[key_index] = list;
+                  grp->num_of_keys++;
+                  return TRUE;
+              }
+           }
+           grp = grp->grp_next;
+     }
+     ALOGE("group does not exist\n");
+     return FALSE;
+  }else {
+     return FALSE;
+  }
+}
diff --git a/libfm_jni/ConfFileParser.h b/libfm_jni/ConfFileParser.h
new file mode 100644
index 0000000..88f67e0
--- /dev/null
+++ b/libfm_jni/ConfFileParser.h
@@ -0,0 +1,82 @@
+/*
+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 __CONF_FILE_PARSER_H__
+#define __CONF_FILE_PARSER_H__
+
+#define MAX_LINE_LEN 512
+#define MAX_UNIQ_KEYS 5
+#define MAX_UNIQ_GRPS 10
+#define TRUE 1
+#define FALSE 0
+
+struct key_value_pair_list
+{
+   char *key;
+   char *value;
+   key_value_pair_list *next;
+};
+
+struct group
+{
+    char *grp_name;
+    unsigned int num_of_keys;
+    unsigned int keys_hash_size;
+    key_value_pair_list **list;
+    group *grp_next;
+};
+
+struct group_table
+{
+    unsigned int grps_hash_size;
+    unsigned int num_of_grps;
+    group **grps_hash;
+};
+
+enum CONF_PARSE_ERRO_CODE
+{
+  PARSE_SUCCESS,
+  INVALID_FILE_NAME,
+  FILE_OPEN_FAILED,
+  FILE_NOT_PROPER,
+  MEMORY_ALLOC_FAILED,
+  PARSE_FAILED,
+};
+
+unsigned int get_hash_code(const char *str);
+group_table *get_key_file();
+void free_strs(char **str_array);
+void free_key_file(group_table *key_file);
+char parse_load_file(group_table *key_file, const char *file);
+char **get_grps(const group_table *key_file);
+char **get_keys(const group_table *key_file, const char *grp);
+char *get_value(const group_table *key_file, const char *grp,
+                 const char *key);
+
+#endif //__CONF_FILE_PARSER_H__
diff --git a/libfm_jni/ConfigFmThs.cpp b/libfm_jni/ConfigFmThs.cpp
new file mode 100644
index 0000000..19ee12b
--- /dev/null
+++ b/libfm_jni/ConfigFmThs.cpp
@@ -0,0 +1,605 @@
+/*
+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 <cstdlib>
+#include <cstring>
+#include <utils/Log.h>
+#include "ConfigFmThs.h"
+#include "FmPerformanceParams.h"
+#include "FmRadioController.h"
+
+static int compare_name
+(
+   const void *name1, const void *name2
+)
+{
+    char *first = (char *)name1;
+    struct NAME_MAP *second = (struct NAME_MAP *)name2;
+
+    return(strcmp(first, second->name));
+}
+
+ConfigFmThs :: ConfigFmThs
+(
+)
+{
+    keyfile = NULL;
+}
+
+ConfigFmThs :: ~ConfigFmThs
+(
+)
+{
+   free_key_file(keyfile);
+}
+
+void ConfigFmThs :: set_band_cfgs
+(
+   UINT fd
+)
+{
+    signed char ret = FM_SUCCESS;
+    char **keys;
+    char **keys_cpy;
+    char *key_value;
+    int value;
+    FmPerformanceParams perf_params;
+    struct NAME_MAP *found;
+
+    if(keyfile != NULL) {
+       keys_cpy = keys = get_keys(keyfile, GRPS_MAP[1].name);
+       if(keys != NULL) {
+          while(*keys != NULL) {
+              ALOGE("key found is: %s\n", *keys);
+              found = (NAME_MAP *)bsearch(*keys, BAND_CFG_MAP,
+                          MAX_BAND_PARAMS, sizeof(NAME_MAP), compare_name);
+              if(found != NULL) {
+                 key_value = get_value(keyfile,
+                                     GRPS_MAP[1].name, found->name);
+                 if((key_value != NULL) && strcmp(key_value, "")) {
+                    value = atoi(key_value);
+                    switch(found->num) {
+                    case RADIO_BAND:
+                         ALOGE("RADIO_BAND\n");
+                         if((value >= BAND_87500_108000)
+                             && (value <= BAND_76000_90000)) {
+                             ALOGE("%s:Set band as: %d\n",__func__, value);
+                             ret = perf_params.SetBand(fd, value);
+                             if(ret == FM_FAILURE)
+                                ALOGE("Error in setting band\n");
+                         }
+                         break;
+                    case EMPHASIS:
+                         ALOGE("EMPHASIS\n");
+                         if((value >= DE_EMP75)
+                             && (value <= DE_EMP50)) {
+                             ALOGE("%s:Set Emphasis as: %d\n",__func__, value);
+                             ret = perf_params.SetEmphsis(fd, value);
+                             if(ret == FM_FAILURE)
+                                ALOGE("Error in setting Emphasis\n");
+                         }
+                         break;
+                    case CHANNEL_SPACING:
+                         ALOGE("CHANNEL_SPACING\n");
+                         if((value >= CHAN_SPACE_200)
+                             && (value <= CHAN_SPACE_50)) {
+                             ALOGE("%s:Set CH space as: %d\n",__func__, value);
+                             ret = perf_params.SetChannelSpacing(fd, value);
+                             if(ret == FM_FAILURE)
+                                ALOGE("Error in setting channel spacing\n");
+                         }
+                         break;
+                   }
+                 }else {
+                   ALOGE("key_val for key: %s is empty\n",
+                             *keys);
+                 }
+                 free(key_value);
+              }
+              keys++;
+          }
+       }else {
+          ALOGE("No of keys found is zero\n");
+       }
+       free_strs(keys_cpy);
+    }else {
+       ALOGE("key file is null\n");
+    }
+}
+
+void ConfigFmThs :: set_af_ths
+(
+   UINT fd
+)
+{
+    signed char ret = FM_SUCCESS;
+    char **keys;
+    char **keys_cpy;
+    char *key_value;
+    int value;
+    FmPerformanceParams perf_params;
+    struct NAME_MAP *found;
+
+    if(keyfile != NULL) {
+       keys_cpy = keys = get_keys(keyfile, GRPS_MAP[0].name);
+       if(keys != NULL) {
+          while(*keys != NULL) {
+              ALOGE("key found is: %s\n", *keys);
+              found = (NAME_MAP *)bsearch(*keys, AF_PARAMS_MAP,
+                          MAX_AF_PARAMS, sizeof(NAME_MAP), compare_name);
+              if(found != NULL) {
+                 key_value = get_value(keyfile,
+                                     GRPS_MAP[0].name, found->name);
+                 if((key_value != NULL) && strcmp(key_value, "")) {
+                    value = atoi(key_value);
+                    switch(found->num) {
+                    case AF_RMSSI_TH:
+                         if((value >= AF_RMSSI_TH_MIN)
+                             && (value <= AF_RMSSI_TH_MAX)) {
+                             ALOGE("Set af rmssi th: %d\n", value);
+                             ret = perf_params.SetAfRmssiTh(fd, value);
+                             if(ret == FM_FAILURE) {
+                                ALOGE("Error in setting Af Rmssi th\n");
+                                break;
+                             }
+                             unsigned short th;
+                             ret = perf_params.GetAfRmssiTh(fd, th);
+                             if(ret == FM_SUCCESS) {
+                                ALOGE("Read af rmssith: %hd\n", th);
+                             }else {
+                                ALOGE("Error in reading Af Rmssi th\n");
+                             }
+                         }
+                         break;
+                    case AF_RMSSI_SAMPLES:
+                         if((value >= AF_RMSSI_SAMPLES_MIN)
+                             && (value <= AF_RMSSI_SAMPLES_MAX)) {
+                             ALOGE("Set af rmssi samples cnt: %d\n", value);
+                             ret = perf_params.SetAfRmssiSamplesCnt(fd, value);
+                             if(ret == FM_FAILURE) {
+                                ALOGE("Error in setting af rmssi samples\n");
+                                break;
+                             }
+                             unsigned char cnt;
+                             ret = perf_params.GetAfRmssiSamplesCnt(fd, cnt);
+                             if(ret == FM_SUCCESS) {
+                                ALOGE("Read af rmssi samples cnt: %hhd\n", cnt);
+                             }else {
+                                 ALOGE("Error in reading rmssi samples\n");
+                             }
+                         }
+                         break;
+                    case GOOD_CH_RMSSI_TH:
+                         if((value >= GOOD_CH_RMSSI_TH_MIN)
+                             && (value <= GOOD_CH_RMSSI_TH_MAX)) {
+                             ALOGE("Set Good channle rmssi th: %d\n", value);
+                             ret = perf_params.SetGoodChannelRmssiTh(fd, value);
+                             if(ret == FM_FAILURE) {
+                                ALOGE("Error in setting Good channle rmssi th\n");
+                                break;
+                             }
+                             signed char th;
+                             ret = perf_params.GetGoodChannelRmssiTh(fd, th);
+                             if(ret == FM_SUCCESS) {
+                                ALOGE("Read good channel rmssi th: %d\n", th);
+                             }else {
+                                ALOGE("Error in reading Good channle rmssi th\n");
+                             }
+                         }
+                         break;
+                   }
+                 }else {
+                   ALOGE("key_val for key: %s is empty\n",
+                             *keys);
+                 }
+                 free(key_value);
+              }
+              keys++;
+          }
+       }else {
+          ALOGE("No of keys found is zero\n");
+       }
+       free_strs(keys_cpy);
+    }else {
+       ALOGE("key file is null\n");
+    }
+}
+
+void ConfigFmThs :: set_srch_ths
+(
+    UINT fd
+)
+{
+    signed char ret = FM_SUCCESS;
+    char **keys = NULL;
+    char **keys_cpy = NULL;
+    char *key_value = NULL;
+    int value;
+    FmPerformanceParams perf_params;
+    struct NAME_MAP *found = NULL;
+
+    if(keyfile != NULL) {
+       keys_cpy = keys = get_keys(keyfile, GRPS_MAP[3].name);
+       if(keys != NULL) {
+          while(*keys != NULL) {
+              found = (NAME_MAP *)bsearch(*keys, SEACH_PARAMS_MAP,
+                           MAX_SRCH_PARAMS, sizeof(NAME_MAP), compare_name);
+              if(found != NULL) {
+                 key_value = get_value(keyfile, GRPS_MAP[2].name, found->name);
+                 ALOGE("found srch ths: %s: %s\n", found->name, key_value);
+                 if((key_value != NULL) && strcmp(key_value, "")) {
+                    value = atoi(key_value);
+                    switch(found->num) {
+                    case SINR_FIRST_STAGE:
+                         if((value >= SINR_FIRST_STAGE_MIN)
+                             && (value <= SINR_FIRST_STAGE_MAX)) {
+                             ALOGE("Set sinr first stage: %d\n", value);
+                             ret = perf_params.SetSinrFirstStage(fd, value);
+                             if(ret == FM_FAILURE) {
+                                ALOGE("Error in setting sinr first stage\n");
+                                break;
+                             }
+                             signed char th;
+                             ret = perf_params.GetSinrFirstStage(fd, th);
+                             if(ret == FM_SUCCESS) {
+                                ALOGE("Read sinr first stage: %d\n", th);
+                             }else {
+                                ALOGE("Error in reading sinr first stage\n");
+                             }
+                         }
+                         break;
+                    case RMSSI_FIRST_STAGE:
+                         if((value >= RMSSI_FIRST_STAGE_MIN)
+                             && (value <= RMSSI_FIRST_STAGE_MAX)) {
+                             ALOGE("Set rmssi first stage: %d\n", value);
+                             ret = perf_params.SetRmssiFirstStage(fd, value);
+                             if(ret == FM_FAILURE) {
+                                ALOGE("Error in setting rmssi first stage\n");
+                                break;
+                             }
+                             signed char th;
+                             ret = perf_params.GetRmssiFirstStage(fd, th);
+                             if(ret == FM_SUCCESS) {
+                                ALOGE("Read rmssi first stage: %d\n", th);
+                             }else {
+                                ALOGE("Error in reading rmssi first stage\n");
+                             }
+                         }
+                         break;
+                    case INTF_LOW_TH:
+                         if((value >= INTF_LOW_TH_MIN)
+                             && (value <= INTF_LOW_TH_MAX)) {
+                            ALOGE("Set intf low th: %d\n", value);
+                            ret = perf_params.SetIntfLowTh(fd, value);
+                            if(ret == FM_FAILURE) {
+                                ALOGE("Error in setting intf low th\n");
+                                break;
+                            }
+                            unsigned char th;
+                            ret = perf_params.GetIntfLowTh(fd, th);
+                            if(ret == FM_SUCCESS) {
+                               ALOGE("Read intf low th: %u\n", th);
+                            }else {
+                               ALOGE("Error in reading intf low th\n");
+                            }
+                         }
+                         break;
+                    case INTF_HIGH_TH:
+                         if((value >= INTF_HIGH_TH_MIN)
+                             && (value <= INTF_HIGH_TH_MAX)) {
+                            ALOGE("Set intf high th: %d\n", value);
+                            ret = perf_params.SetIntfHighTh(fd, value);
+                            if(ret == FM_FAILURE) {
+                                ALOGE("Error in setting intf high th\n");
+                                break;
+                            }
+                            unsigned char th;
+                            ret = perf_params.GetIntfHighTh(fd, th);
+                            if(ret == FM_SUCCESS) {
+                               ALOGE("Read intf high th: %u\n", th);
+                            }else {
+                               ALOGE("Error in reading intf high th\n");
+                            }
+                         }
+                         break;
+                    case CF0_TH:
+                         ALOGE("Set cf0 th: %d\n", value);
+                         ret = perf_params.SetCf0Th12(fd, value);
+                         if(ret == FM_FAILURE) {
+                            ALOGE("Error in setting cf0 th\n");
+                            break;
+                         }
+                         int th;
+                         ret = perf_params.GetCf0Th12(fd, th);
+                         if(ret == FM_SUCCESS) {
+                            ALOGE("Read CF012 th: %d\n", th);
+                         }else {
+                            ALOGE("Error in reading cf0 th\n");
+                         }
+                         break;
+                    case SRCH_ALGO_TYPE:
+                         if((value >= SRCH_ALGO_TYPE_MIN)
+                             && (value <= SRCH_ALGO_TYPE_MAX)) {
+                             ALOGE("Set search algo type: %d\n", value);
+                             ret = perf_params.SetSrchAlgoType(fd, value);
+                             if(ret == FM_FAILURE) {
+                                ALOGE("Error in setting search algo type\n");
+                                break;
+                             }
+                             unsigned char algo;
+                             ret = perf_params.GetSrchAlgoType(fd, algo);
+                             if(ret == FM_SUCCESS) {
+                                ALOGE("Read algo type: %u\n", algo);
+                             }else {
+                                ALOGE("Error in reading search algo type\n");
+                             }
+                         }
+                         break;
+                    case SINR_SAMPLES:
+                         if((value >= SINR_SAMPLES_CNT_MIN)
+                             && (value <= SINR_SAMPLES_CNT_MAX)) {
+                             ALOGE("Set sinr samples count: %d\n", value);
+                             ret = perf_params.SetSinrSamplesCnt(fd, value);
+                             if(ret == FM_FAILURE) {
+                                ALOGE("Error in setting sinr samples count\n");
+                                break;
+                             }
+                             unsigned char cnt;
+                             ret = perf_params.GetSinrSamplesCnt(fd, cnt);
+                             if(ret == FM_SUCCESS) {
+                                ALOGE("Read sinr samples cnt: %u\n", cnt);
+                             }else {
+                                ALOGE("Error in reading sinr samples count\n");
+                             }
+                         }
+                         break;
+                    case SINR:
+                         if((value >= SINR_FINAL_STAGE_MIN)
+                             && (value <= SINR_FINAL_STAGE_MAX)) {
+                             ALOGE("Set final stage sinr: %d\n", value);
+                             ret = perf_params.SetSinrFinalStage(fd, value);
+                             if(ret == FM_FAILURE) {
+                                ALOGE("Error in setting final stage sinr\n");
+                                break;
+                             }
+                             signed char th;
+                             ret = perf_params.GetSinrFinalStage(fd, th);
+                             if(ret == FM_SUCCESS) {
+                                ALOGE("Read final stage sinr: %d\n", th);
+                             }else {
+                                ALOGE("Error in reading final stage sinr\n");
+                             }
+                         }
+                         break;
+                    }
+                 }else {
+                    ALOGE("key_value for key: %s is empty\n",
+                                  *keys);
+                 }
+                 free(key_value);
+              }
+              keys++;
+          }
+       }else {
+          ALOGE("No of keys found is zero\n");
+       }
+       free_strs(keys_cpy);
+    }else {
+       ALOGE("key file is null\n");
+    }
+}
+
+void ConfigFmThs :: set_hybrd_list
+(
+    UINT fd
+)
+{
+    signed char ret = FM_SUCCESS;
+    char **keys = NULL;
+    char **keys_cpy = NULL;
+    char *key_value = NULL;
+    char *freqs = NULL;
+    unsigned int *freqs_array = NULL;
+    signed char *sinrs_array = NULL;
+    char *sinrs = NULL;
+    int value;
+    unsigned int freq_cnt = 0;
+    unsigned int sinr_cnt = 0;
+    FmPerformanceParams perf_params;
+    struct NAME_MAP *found;
+
+    ALOGE("Inside hybrid srch list\n");
+    if(keyfile != NULL) {
+       keys_cpy = keys = get_keys(keyfile, GRPS_MAP[2].name);
+       if(keys != NULL) {
+          while(*keys != NULL) {
+              found = (NAME_MAP *)bsearch(*keys, HYBRD_SRCH_MAP,
+                           MAX_HYBRID_SRCH_PARAMS, sizeof(NAME_MAP), compare_name);
+              if(found != NULL) {
+                 key_value = get_value(keyfile, GRPS_MAP[1].name, found->name);
+                 if((key_value != NULL) && strcmp(key_value, "")) {
+                     switch(found->num) {
+                     case FREQ_LIST:
+                          freqs = key_value;
+                          break;
+                     case SINR_LIST:
+                          sinrs = key_value;
+                          break;
+                     default:
+                          free(key_value);
+                          break;
+                     }
+                 }
+              }
+              keys++;
+          }
+          free_strs(keys_cpy);
+       }else {
+          ALOGE("No of keys found is zero\n");
+       }
+    }else {
+       ALOGE("key file is null\n");
+    }
+
+    freq_cnt = extract_comma_sep_freqs(freqs, &freqs_array, ",");
+    sinr_cnt = extract_comma_sep_sinrs(sinrs, &sinrs_array, ",");
+
+    if((freq_cnt == sinr_cnt) && (sinr_cnt > 0)) {
+       perf_params.SetHybridSrchList(fd, freqs_array, sinrs_array, freq_cnt);
+    }
+
+    free(freqs);
+    free(sinrs);
+    free(freqs_array);
+    free(sinrs_array);
+}
+
+unsigned int ConfigFmThs :: extract_comma_sep_freqs
+(
+    char *freqs,
+    unsigned int **freqs_arr,
+    const char *str
+)
+{
+    char *next_freq;
+    unsigned int freq;
+    unsigned int *freqs_new_arr;
+    unsigned int size = 0;
+    unsigned int len = 0;
+
+    next_freq = strtok(freqs, str);
+    while(next_freq != NULL) {
+          freq = atoi(next_freq);
+          ALOGD("HYBRID_SRCH freq: %u\n", freq);
+          if(size == len) {
+             size <<= 1;
+             if(size == 0)
+                size = 1;
+             freqs_new_arr = (unsigned int *)realloc(*freqs_arr,
+                                              size * sizeof(unsigned int));
+             if(freqs_new_arr == NULL) {
+                free(*freqs_arr);
+                *freqs_arr = NULL;
+                break;
+             }
+             *freqs_arr = freqs_new_arr;
+          }
+          (*freqs_arr)[len] = freq;
+          len++;
+          next_freq = strtok(NULL, str);
+    }
+    return len;
+}
+
+unsigned int ConfigFmThs :: extract_comma_sep_sinrs
+(
+    char *sinrs,
+    signed char **sinrs_arr,
+    const char *str
+)
+{
+    char *next_sinr;
+    signed char *sinrs_new_arr;
+    unsigned int size = 0;
+    unsigned int len = 0;
+    signed char sinr;
+
+    next_sinr = strtok(sinrs, str);
+    while(next_sinr != NULL) {
+          sinr = atoi(next_sinr);
+          ALOGD("HYBRID_SRCH sinr: %d\n", sinr);
+          if(size == len) {
+             size <<= 1;
+             if(size == 0)
+                size = 1;
+             sinrs_new_arr = (signed char *)realloc(*sinrs_arr,
+                                               size * sizeof(signed char));
+             if(sinrs_new_arr == NULL) {
+                free(*sinrs_arr);
+                *sinrs_arr = NULL;
+                break;
+             }
+             *sinrs_arr = sinrs_new_arr;
+          }
+          (*sinrs_arr)[len] = sinr;
+          len++;
+          next_sinr = strtok(NULL, str);
+    }
+    return len;
+}
+
+void  ConfigFmThs :: SetRxSearchAfThs
+(
+    const char *file, UINT fd
+)
+{
+    int index;
+    struct NAME_MAP *found;
+    char **grps = NULL;
+    char **grps_cpy = NULL;
+
+    keyfile = get_key_file();
+
+    ALOGE("file name is: %s\n", file);
+    if(!parse_load_file(keyfile, file)) {
+       ALOGE("Error in loading threshold file\n");
+    }else {
+       grps_cpy = grps = get_grps(keyfile);
+       if(grps != NULL) {
+          while(*grps != NULL) {
+              ALOGE("Search grp: %s\n", *grps);
+              found = (NAME_MAP *)bsearch(*grps, GRPS_MAP, MAX_GRPS,
+                             sizeof(NAME_MAP), compare_name);
+              if(found != NULL) {
+                 ALOGE("Found group: %s\n", found->name);
+                 switch(found->num) {
+                 case AF_THS:
+                      set_af_ths(fd);
+                      break;
+                 case SRCH_THS:
+                      set_srch_ths(fd);
+                      break;
+                 case HYBRD_SRCH_LIST:
+                      set_hybrd_list(fd);
+                      break;
+                 case BAND_CFG:
+                      set_band_cfgs(fd);
+                      break;
+                 }
+              }
+              grps++;
+          }
+       }else {
+          ALOGE("No of groups found is zero\n");
+       }
+       free_strs(grps_cpy);
+    }
+    free_key_file(keyfile);
+    keyfile = NULL;
+}
diff --git a/libfm_jni/ConfigFmThs.h b/libfm_jni/ConfigFmThs.h
new file mode 100644
index 0000000..19bf86c
--- /dev/null
+++ b/libfm_jni/ConfigFmThs.h
@@ -0,0 +1,183 @@
+/*
+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 __CONFIG_FM_THS_H__
+#define __CONFIG_FM_THS_H__
+
+#include <cstring>
+#include "FM_Const.h"
+#include "ConfFileParser.h"
+
+#define MAX_GRPS 4
+#define MAX_SRCH_PARAMS 8
+#define MAX_AF_PARAMS 3
+#define MAX_BAND_PARAMS 3
+
+#define SINR_SAMPLES_CNT_MIN 0
+#define SINR_SAMPLES_CNT_MAX 255
+#define SINR_FIRST_STAGE_MIN -128
+#define SINR_FIRST_STAGE_MAX 127
+#define RMSSI_FIRST_STAGE_MIN -128
+#define RMSSI_FIRST_STAGE_MAX 127
+#define INTF_LOW_TH_MIN 0
+#define INTF_LOW_TH_MAX  255
+#define INTF_HIGH_TH_MIN 0
+#define INTF_HIGH_TH_MAX 255
+#define SRCH_ALGO_TYPE_MIN 0
+#define SRCH_ALGO_TYPE_MAX 1
+#define SINR_FINAL_STAGE_MIN -128
+#define SINR_FINAL_STAGE_MAX 127
+
+#define AF_RMSSI_TH_MIN 0
+#define AF_RMSSI_TH_MAX 65535
+#define AF_RMSSI_SAMPLES_MIN 0
+#define AF_RMSSI_SAMPLES_MAX 255
+#define GOOD_CH_RMSSI_TH_MIN -128
+#define GOOD_CH_RMSSI_TH_MAX 127
+#define FM_DE_EMP75  0
+#define FM_DE_EMP50  1
+#define FM_CHSPACE_200_KHZ  0
+#define FM_CHSPACE_100_KHZ  1
+#define FM_CHSPACE_50_KHZ  2
+
+const unsigned char MAX_HYBRID_SRCH_PARAMS = 2;
+
+struct NAME_MAP
+{
+   const char name[50];
+   const int num;
+};
+
+enum PERFORMANCE_GRPS
+{
+    AF_THS,
+    SRCH_THS,
+    HYBRD_SRCH_LIST,
+    BAND_CFG,
+};
+
+enum BAND_CFG_PARAMS
+{
+    RADIO_BAND,
+    EMPHASIS,
+    CHANNEL_SPACING,
+};
+
+enum PERFORMANCE_SRCH_PARAMS
+{
+    SRCH_ALGO_TYPE,
+    CF0_TH,
+    SINR_FIRST_STAGE,
+    SINR,
+    RMSSI_FIRST_STAGE,
+    INTF_LOW_TH,
+    INTF_HIGH_TH,
+    SINR_SAMPLES,
+};
+
+enum PERFORMANCE_AF_PARAMS
+{
+    AF_RMSSI_TH,
+    AF_RMSSI_SAMPLES,
+    GOOD_CH_RMSSI_TH,
+};
+
+enum HYBRID_SRCH_PARAMS
+{
+    FREQ_LIST,
+    SINR_LIST,
+};
+
+//Keep this list in sorted order (ascending order in terms of "name")
+//Don't change the name of GRPS, if changed please also change accordingly
+//file: fm_srch_af_th.conf
+static struct NAME_MAP GRPS_MAP[] =
+{
+   {"AFTHRESHOLDS", AF_THS},
+   {"BANDCONFIG", BAND_CFG},
+   {"HYBRIDSEARCHLIST", HYBRD_SRCH_LIST},
+   {"SEARCHTHRESHOLDS", SRCH_THS},
+};
+
+static struct NAME_MAP BAND_CFG_MAP[] =
+{
+    {"ChSpacing", CHANNEL_SPACING},
+    {"Emphasis", EMPHASIS},
+    {"RadioBand", RADIO_BAND},
+};
+
+//Keep this list in sorted order (ascending order in terms of "name")
+//Don't change the name of SEARCH thresholds,
+//if changed please also change accordingly
+//file: fm_srch_af_th.conf
+static struct NAME_MAP SEACH_PARAMS_MAP[] =
+{
+   {"Cf0Th12", CF0_TH},
+   {"IntfHighTh", INTF_HIGH_TH},
+   {"IntfLowTh", INTF_LOW_TH},
+   {"RmssiFirstStage", RMSSI_FIRST_STAGE},
+   {"SearchAlgoType", SRCH_ALGO_TYPE},
+   {"Sinr", SINR},
+   {"SinrFirstStage", SINR_FIRST_STAGE},
+   {"SinrSamplesCnt", SINR_SAMPLES},
+};
+
+//Keep this list in sorted order (ascending order in terms of "name")
+//Don't change the name of SEARCH thresholds,
+//if changed please also change accordingly
+//file: fm_srch_af_th.conf
+static struct NAME_MAP AF_PARAMS_MAP[] =
+{
+   {"AfRmssiSamplesCnt", AF_RMSSI_SAMPLES},
+   {"AfRmssiTh", AF_RMSSI_TH},
+   {"GoodChRmssiTh", GOOD_CH_RMSSI_TH},
+};
+
+static struct NAME_MAP HYBRD_SRCH_MAP[] =
+{
+   {"Freqs", FREQ_LIST},
+   {"Sinrs", SINR_LIST},
+};
+
+class ConfigFmThs {
+   private:
+          group_table *keyfile;
+          void set_srch_ths(UINT fd);
+          void set_af_ths(UINT fd);
+          unsigned int extract_comma_sep_freqs(char *freqs, unsigned int **freqs_arr, const char *str);
+          unsigned int extract_comma_sep_sinrs(char *sinrs, signed char **sinrs_arr, const char *str);
+          void set_hybrd_list(UINT fd);
+          void set_band_cfgs(UINT fd);
+   public:
+          ConfigFmThs();
+          ~ConfigFmThs();
+          void SetRxSearchAfThs(const char *file, UINT fd);
+};
+
+#endif //__CONFIG_FM_THS_H__
diff --git a/libfm_jni/FM_Const.h b/libfm_jni/FM_Const.h
new file mode 100644
index 0000000..a93fe77
--- /dev/null
+++ b/libfm_jni/FM_Const.h
@@ -0,0 +1,368 @@
+/*
+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 __FM_CONST_H__
+#define __FM_CONST_H__
+
+//return related
+#define IOCTL_SUCC 0
+#define FM_SUCCESS 0
+#define FM_FAILURE -1
+#define PROP_SET_SUCC 0
+
+#define MAX_VER_STR_LEN 40
+#define INIT_LOOP_CNT 45
+
+//Time in us
+#define INIT_WAIT_TIMEOUT 200000
+#define RDS_AVL_INT_WAIT_TIMEOUT 200000
+//Time in secs
+#define READY_EVENT_TIMEOUT 5
+#define SCAN_COMPL_TIMEOUT 1280
+#define TUNE_EVENT_TIMEOUT 2
+#define SEEK_COMPL_TIMEOUT 60
+#define FM_SCAN_CH_SIZE_MAX 25
+
+#define TUNE_MULT 16
+#define CAL_DATA_SIZE 23
+#define STD_BUF_SIZE 256
+
+//RDS GROUPS
+#define GRP_3A 64
+
+//data related to enabling various
+//rds data RT, PS, AF LIST, AF JMP
+#define RDS_GRP_RT_EN 1
+#define RDS_GRP_PS_EN 2
+#define RDS_AF_JMP_LIST_EN 8
+#define RDS_GRP_AFJMP_EN 16
+
+//Data for PS received
+#define MAX_PS_LEN 8
+#define PS_STR_NUM_IND 0
+#define PS_DATA_OFFSET_IND 5
+
+//RT related
+#define MAX_RT_LEN 64
+#define RT_LEN_IND 0
+#define RT_DATA_OFFSET_IND 5
+#define RT_A_B_FLAG_IND 4
+
+//ERT related
+#define ERT_LEN_IND 0
+#define ERT_DATA_OFFSET_IND 3
+
+//Common to RT, PS
+#define FIRST_CTRL_CHAR 0
+#define LAST_CTRL_CHAR 31
+#define FIRST_NON_PRNT_CHAR  127
+#define SPACE_CHAR 32
+
+//RT PLUS related constanst
+#define RT_OR_ERT_IND 1
+#define RT_PLUS_TAGS_NUM_IND 0
+#define MAX_RT_PLUS_TAGS 2
+#define TAGS_DATA_BEGIN_OFFSET 4
+#define ITEM_TOGGLE_IND 2
+#define ITEM_RUNNING_IND 3
+#define DUMMY_TAG_CODE 0
+
+#define FM_RX_RDS_GRP_RT_EBL   1
+#define FM_RX_RDS_GRP_PS_EBL   2
+#define FM_RX_RDS_GRP_AF_EBL 4
+#define FM_RX_RDS_GRP_PS_SIMPLE_EBL 16
+
+#define FREQ_MULT 1000
+#define HYBRID_SRCH_DATA_LEN 3
+#define HYBRID_SRCH_DATA_INDEX 3
+#define HYBRID_SRCH_MODE 0x40
+#define STARTING_FREQ 76000
+#define FREQ_MULTIPLEX 50
+#define NO_OF_BYTES_EACH_FREQ 2
+#define EXTRACT_FIRST_BYTE 0x03
+#define SRCH_DIV 100
+#define AF_SIZE_IDX 6
+#define NO_OF_BYTES_AF 4
+#define MAX_AF_LIST_SIZE 25
+
+typedef  unsigned int UINT;
+typedef  unsigned long ULINT;
+
+//STRING LITERALS
+const char *const FM_MODE_PROP = "hw.fm.mode";
+const char *const FM_VERSION_PROP = "hw.fm.version";
+const char *const FM_INIT_PROP = "hw.fm.init";
+const char *const SCRIPT_START_PROP = "ctl.start";
+const char *const FM_SOC_DL_SCRIPT = "fm_dl";
+const char *const SCRIPT_STOP_PROP = "ctl.stOP";
+const char *const CALIB_DATA_NAME = "/data/app/Riva_fm_cal";
+const char *const SOC_PATCH_DL_SCRPT = "fm_dl";
+const char *const FM_DEVICE_PATH = "/dev/radio0";
+const char *const FM_PERFORMANCE_PARAMS = "/etc/fm/fm_srch_af_th.conf";
+
+const UINT V4L2_CTRL_CLASS_USER = 0x00980000;
+const UINT V4L2_CID_BASE = (V4L2_CTRL_CLASS_USER | 0x900);
+const UINT V4L2_CID_AUDIO_MUTE  = (V4L2_CID_BASE + 9);
+const UINT SEARCH_DWELL_TIME = 2;
+const UINT SEEK_DWELL_TIME = 0;
+
+//BAND LIMITS
+const UINT US_BAND_LOW = 87500;
+const UINT US_BAND_HIGH = 108000;
+const UINT EU_BAND_LOW = 87500;
+const UINT EU_BAND_HIGH = 108000;
+const UINT JAP_BAND_STD_LOW = 76000;
+const UINT JAP_BAND_STD_HIGH = 90000;
+const UINT JAP_BAND_WIDE_LOW = 90000;
+const UINT JAP_BAND_WIDE_HIGH = 108000;
+const UINT USR_DEF_BAND_LOW = 87500;
+const UINT USR_DEF_BAND_HIGH = 108000;
+const UINT FM_RX_SRCHLIST_MAX_STATIONS =20;
+
+//RDS standard type
+enum RDS_STD
+{
+    RBDS,
+    RDS,
+    NO_RDS_RBDS,
+};
+
+enum DE_EMPHASIS
+{
+    DE_EMP75,
+    DE_EMP50,
+};
+
+//Band Type
+enum FM_REGION
+{
+    BAND_87500_108000 = 1,
+    BAND_76000_108000,
+    BAND_76000_90000,
+};
+
+enum FM_AUDIO_PATH
+{
+    AUDIO_DIGITAL_PATH,
+    AUDIO_ANALOG_PATH,
+};
+
+enum FM_DEVICE
+{
+    FM_DEV_NONE,
+    FM_RX,
+    FM_TX,
+};
+
+enum fm_rds_onoff {
+    FM_RDS_ON,
+    FM_RDS_OFF,
+    FM_MAX
+};
+
+typedef enum {
+    FM_LONG_ANA = 0,
+    FM_SHORT_ANA
+} fm_antenna_type;
+
+enum BUFF_INDEXES
+{
+    STATION_LIST_IND,
+    EVENT_IND,
+    RT_IND,
+    PS_IND,
+    AF_LIST_IND = PS_IND + 2,
+    RT_PLUS_IND = 11,
+    ERT_IND,
+};
+
+typedef enum {
+    RDS_EVT_UPDATE         = 0x0001,
+    RDS_EVT_PI_UPDATE      = 0x0002,
+    RDS_EVT_PTY_UPDATE     = 0x0004,
+    RDS_EVT_PS_UPDATE      = 0x0008,
+    RDS_EVT_RT_UPDATE      = 0x0040,
+    RDS_EVT_AF_JUMP        = 0x0080,
+    RDS_EVT_AF_LIST        = 0x0100,
+    RDS_EVT_AF_LIST_UPDATE = 0x0200,
+    RDS_EVT_RDS_AVL        = 0x2000,
+    RDS_EVT_RDS_NOT_AVL    = 0x4000,
+} RdsEvts;
+
+enum SEARCH_MODE
+{
+    SEEK_MODE,
+    SCAN_MODE,
+    SRCHLIST_MODE_STRONG,
+    SRCHLIST_MODE_WEAK,
+    SRCHRDS_MODE_SEEK_PTY,
+    SRCHRDS_MODE_SCAN_PTY,
+    SRCHRDS_MODE_SEEK_PI,
+    SRCHRDS_MODE_SEEK_AF,
+    SRCHLIST_MODE_STRONGEST,
+    SRCHLIST_MODE_WEAKEST,
+};
+
+enum SEARCH_DIR
+{
+    SEARCH_DOWN,
+    SEARCH_UP,
+};
+
+//CHANNEL SPACING for samsung
+enum SS_CHAN_SPACING
+{
+    SS_CHAN_SPACE_200 = 20,
+    SS_CHAN_SPACE_100 = 10,
+    SS_CHAN_SPACE_50 = 5,
+};
+
+enum CHAN_SPACING
+{
+    CHAN_SPACE_200,
+    CHAN_SPACE_100,
+    CHAN_SPACE_50,
+};
+
+enum AUDIO_MODE
+{
+    MONO,
+    STEREO,
+};
+
+//HARD MUTE MODE
+enum HARD_MUTE_MODE
+{
+    UNMUTE_L_R_CHAN,
+    MUTE_L_CHAN,
+    MUTE_R_CHAN,
+    MUTE_L_R_CHAN,
+};
+
+//SOFT MUTE STATES
+enum SOFT_MUTE_MODE
+{
+    SMUTE_DISABLED,
+    SMUTE_ENABLED,
+};
+
+//FM EVENTS FROM KERNEL
+enum FM_EVENTS
+{
+    READY_EVENT,
+    TUNE_EVENT,
+    SEEK_COMPLETE_EVENT,
+    SCAN_NEXT_EVENT,
+    RAW_RDS_EVENT,
+    RT_EVENT,
+    PS_EVENT,
+    ERROR_EVENT,
+    BELOW_TH_EVENT,
+    ABOVE_TH_EVENT,
+    STEREO_EVENT,
+    MONO_EVENT,
+    RDS_AVAL_EVENT,
+    RDS_NOT_AVAL_EVENT,
+    SRCH_LIST_EVENT,
+    AF_LIST_EVENT,
+    DISABLED_EVENT = 18,
+    RDS_GRP_MASK_REQ_EVENT,
+    RT_PLUS_EVENT,
+    ERT_EVENT,
+    AF_JMP_EVENT,
+};
+
+//FM STATES
+enum fm_states
+{
+    FM_OFF,
+    FM_OFF_IN_PROGRESS,
+    FM_ON_IN_PROGRESS,
+    FM_ON,
+    FM_TUNE_IN_PROGRESS,
+    SEEK_IN_PROGRESS,
+    SCAN_IN_PROGRESS,
+};
+
+//V4L2 CONTROLS FOR FM DRIVER
+enum FM_V4L2_PRV_CONTROLS
+{
+    V4L2_CID_PRV_BASE = 0x8000000,
+    V4L2_CID_PRV_SRCHMODE,
+    V4L2_CID_PRV_SCANDWELL,
+    V4L2_CID_PRV_SRCHON,
+    V4L2_CID_PRV_STATE,
+    V4L2_CID_PRV_TRANSMIT_MODE,
+    V4L2_CID_PRV_RDSGROUP_MASK,
+    V4L2_CID_PRV_REGION,
+    V4L2_CID_PRV_SIGNAL_TH,
+    V4L2_CID_PRV_SRCH_PTY,
+    V4L2_CID_PRV_SRCH_PI,
+    V4L2_CID_PRV_SRCH_CNT,
+    V4L2_CID_PRV_EMPHASIS,
+    V4L2_CID_PRV_RDS_STD,
+    V4L2_CID_PRV_CHAN_SPACING,
+    V4L2_CID_PRV_RDSON,
+    V4L2_CID_PRV_RDSGROUP_PROC,
+    V4L2_CID_PRV_LP_MODE,
+    V4L2_CID_PRV_ANTENNA,
+    V4L2_CID_PRV_INTDET = V4L2_CID_PRV_BASE + 25,
+    V4L2_CID_PRV_AF_JUMP = V4L2_CID_PRV_BASE + 27,
+    V4L2_CID_PRV_SOFT_MUTE = V4L2_CID_PRV_BASE + 30,
+    V4L2_CID_PRV_AUDIO_PATH = V4L2_CID_PRV_BASE + 41,
+    V4L2_CID_PRV_SINR = V4L2_CID_PRV_BASE + 44,
+    V4L2_CID_PRV_ON_CHANNEL_THRESHOLD = V4L2_CID_PRV_BASE + 0x2D,
+    V4L2_CID_PRV_OFF_CHANNEL_THRESHOLD,
+    V4L2_CID_PRV_SINR_THRESHOLD,
+    V4L2_CID_PRV_SINR_SAMPLES,
+    V4L2_CID_PRV_SPUR_FREQ,
+    V4L2_CID_PRV_SPUR_FREQ_RMSSI,
+    V4L2_CID_PRV_SPUR_SELECTION,
+    V4L2_CID_PRV_AF_RMSSI_TH = V4L2_CID_PRV_BASE + 0x36,
+    V4L2_CID_PRV_AF_RMSSI_SAMPLES,
+    V4L2_CID_PRV_GOOD_CH_RMSSI_TH,
+    V4L2_CID_PRV_SRCHALGOTYPE,
+    V4L2_CID_PRV_CF0TH12,
+    V4L2_CID_PRV_SINRFIRSTSTAGE,
+    V4L2_CID_PRV_RMSSIFIRSTSTAGE,
+    V4L2_CID_PRV_SOFT_MUTE_TH,
+    V4L2_CID_PRV_IRIS_RDSGRP_RT,
+    V4L2_CID_PRV_IRIS_RDSGRP_PS_SIMPLE,
+    V4L2_CID_PRV_IRIS_BLEND_SINRHI,
+    V4L2_CID_PRV_IRIS_BLEND_RMSSIHI,
+    V4L2_CID_PRV_IRIS_RDSGRP_AFLIST,
+    V4L2_CID_PRV_IRIS_RDSGRP_ERT,
+    V4L2_CID_PRV_IRIS_RDSGRP_RT_PLUS,
+    V4L2_CID_PRV_IRIS_RDSGRP_3A,
+
+    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,
+};
+#endif
diff --git a/libfm_jni/FmIoctlsInterface.cpp b/libfm_jni/FmIoctlsInterface.cpp
new file mode 100644
index 0000000..bd3440b
--- /dev/null
+++ b/libfm_jni/FmIoctlsInterface.cpp
@@ -0,0 +1,384 @@
+/*
+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 "FmIoctlsInterface.h"
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <fcntl.h>
+#include <cutils/properties.h>
+#include <sys/ioctl.h>
+#include <linux/videodev2.h>
+#include <math.h>
+#include <utils/Log.h>
+
+int FmIoctlsInterface :: start_fm_patch_dl
+(
+    UINT fd
+)
+{
+    int ret;
+    int init_success = 0;
+    char versionStr[MAX_VER_STR_LEN] = {'\0'};
+    char prop_value[PROPERTY_VALUE_MAX] = {'\0'};
+    struct v4l2_capability cap;
+
+    ALOGE("start_fm_patch_dl = %d\n",fd);
+    ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
+    ALOGE("executed cmd\n");
+    if(ret == IOCTL_SUCC) {
+        ret = snprintf(versionStr, MAX_VER_STR_LEN, "%d", cap.version);
+        if(ret >= MAX_VER_STR_LEN) {
+            return FM_FAILURE;
+        }else {
+            ret = property_set(FM_VERSION_PROP, versionStr);
+            ALOGE("set versionStr done");
+            if(ret != PROP_SET_SUCC)
+               return FM_FAILURE;
+            ret = property_set(FM_MODE_PROP, "normal");
+            ALOGE("set FM_MODE_PROP done");
+            if(ret != PROP_SET_SUCC)
+               return FM_FAILURE;
+            ret = property_set(FM_INIT_PROP, "0");
+            ALOGE("set FM_INIT_PROP done");
+            if(ret != PROP_SET_SUCC)
+               return FM_FAILURE;
+            ret = property_set(SCRIPT_START_PROP, SOC_PATCH_DL_SCRPT);
+            if(ret != PROP_SET_SUCC)
+               return FM_FAILURE;
+            for(int i = 0; i < INIT_LOOP_CNT; i++) {
+                property_get(FM_INIT_PROP, prop_value, NULL);
+                if (strcmp(prop_value, "1") == 0) {
+                    init_success = 1;
+                    break;
+                }else {
+                    usleep(INIT_WAIT_TIMEOUT);
+                }
+            }
+            if(!init_success) {
+                property_set(SCRIPT_STOP_PROP, SOC_PATCH_DL_SCRPT);
+                return FM_FAILURE;
+            }else {
+                return FM_SUCCESS;
+            }
+        }
+    }else {
+        return FM_FAILURE;
+    }
+}
+
+int  FmIoctlsInterface :: close_fm_patch_dl
+(
+    void
+)
+{
+    int ret;
+
+    ret = property_set(SCRIPT_STOP_PROP, SOC_PATCH_DL_SCRPT);
+    if(ret != PROP_SET_SUCC) {
+        return FM_FAILURE;
+    }else {
+        return FM_SUCCESS;
+    }
+}
+
+int  FmIoctlsInterface :: get_cur_freq
+(
+    UINT fd, long &freq
+)
+{
+    int ret;
+    struct v4l2_frequency channel;
+
+    channel.type = V4L2_TUNER_RADIO;
+    ret = ioctl(fd, VIDIOC_G_FREQUENCY, &channel);
+
+    if(ret != IOCTL_SUCC) {
+        return FM_FAILURE;
+    }else {
+        freq = (channel.frequency / TUNE_MULT);
+        return FM_SUCCESS;
+    }
+}
+
+int  FmIoctlsInterface :: set_freq
+(
+    UINT fd, ULINT freq
+)
+{
+    int ret;
+    struct v4l2_frequency channel;
+
+    channel.type = V4L2_TUNER_RADIO;
+    channel.frequency = (freq * TUNE_MULT);
+
+    ret = ioctl(fd, VIDIOC_S_FREQUENCY, &channel);
+    if(ret != IOCTL_SUCC) {
+        return FM_FAILURE;
+    }else {
+        return FM_SUCCESS;
+    }
+}
+
+int  FmIoctlsInterface :: set_control
+(
+    UINT fd, UINT id, int val
+)
+{
+    int ret;
+    struct v4l2_control control;
+
+    control.value = val;
+    control.id = id;
+
+    ret = ioctl(fd, VIDIOC_S_CTRL, &control);
+    if(ret != IOCTL_SUCC) {
+        return FM_FAILURE;
+    }else {
+        return FM_SUCCESS;
+    }
+}
+
+int  FmIoctlsInterface :: set_calibration
+(
+    UINT fd
+)
+{
+    int ret;
+    FILE *cal_fp;
+    struct v4l2_ext_control ext_ctl;
+    struct v4l2_ext_controls v4l2_ctls;
+    char cal_data[CAL_DATA_SIZE] = {0};
+
+    cal_fp = fopen(CALIB_DATA_NAME, "r");
+    if(cal_fp != NULL) {
+       if(fread(&cal_data[0], 1, CAL_DATA_SIZE, cal_fp)
+           < CAL_DATA_SIZE) {
+           fclose(cal_fp);
+           return FM_FAILURE;
+       }
+       fclose(cal_fp);
+       ext_ctl.string = cal_data;
+       ext_ctl.size = CAL_DATA_SIZE;
+       v4l2_ctls.ctrl_class = V4L2_CTRL_CLASS_USER;
+       v4l2_ctls.count = 1;
+       v4l2_ctls.controls = &ext_ctl;
+       ret = ioctl(fd, VIDIOC_S_EXT_CTRLS, &v4l2_ctls);
+       if(ret != IOCTL_SUCC) {
+           return FM_FAILURE;
+       }else {
+           return FM_SUCCESS;
+       }
+    }else {
+        return FM_FAILURE;
+    }
+}
+
+int  FmIoctlsInterface :: get_control
+(
+    UINT fd, UINT id, long &val
+)
+{
+    int ret;
+    struct v4l2_control control;
+
+    control.id = id;
+    ret = ioctl(fd, VIDIOC_G_CTRL, &control);
+    if(ret != IOCTL_SUCC) {
+        return FM_FAILURE;
+    }else {
+        val = control.value;
+        return FM_SUCCESS;
+    }
+}
+
+int  FmIoctlsInterface :: start_search
+(
+    UINT fd, UINT dir
+)
+{
+    int ret;
+    struct v4l2_hw_freq_seek hw_seek;
+
+    hw_seek.seek_upward = dir;
+    hw_seek.type = V4L2_TUNER_RADIO;
+
+    ret = ioctl(fd, VIDIOC_S_HW_FREQ_SEEK, &hw_seek);
+    if(ret != IOCTL_SUCC) {
+        return FM_FAILURE;
+    }else {
+        return FM_SUCCESS;
+    }
+}
+
+int  FmIoctlsInterface :: set_band
+(
+    UINT fd, ULINT low, ULINT high
+)
+{
+    int ret;
+    struct v4l2_tuner tuner;
+
+    tuner.index = 0;
+    tuner.signal = 0;
+    tuner.rangelow = (low * TUNE_MULT);
+    tuner.rangehigh = (high * TUNE_MULT);
+
+    ret = ioctl(fd, VIDIOC_S_TUNER, &tuner);
+    ret = set_control(fd, V4L2_CID_PRV_REGION, 0);
+    if(ret != IOCTL_SUCC) {
+        return FM_FAILURE;
+    }else {
+        return FM_SUCCESS;
+    }
+}
+
+int FmIoctlsInterface :: get_rmssi
+(
+    UINT fd, long &rmssi
+)
+{
+    struct v4l2_tuner tuner;
+    int ret;
+
+    tuner.index = 0;
+    tuner.signal = 0;
+    ret = ioctl(fd, VIDIOC_G_TUNER, &tuner);
+    if(ret != IOCTL_SUCC) {
+        ret = FM_SUCCESS;
+    }else {
+        rmssi = tuner.signal;
+        ret = FM_SUCCESS;
+    }
+    return ret;
+}
+
+int  FmIoctlsInterface :: get_upperband_limit
+(
+    UINT fd, ULINT &freq
+)
+{
+    int ret;
+    struct v4l2_tuner tuner;
+
+    tuner.index = 0;
+    ret = ioctl(fd, VIDIOC_G_TUNER, &tuner);
+    if(ret != IOCTL_SUCC) {
+        return FM_FAILURE;
+    }else {
+        freq = (tuner.rangehigh / TUNE_MULT);
+        ALOGE("high freq: %d\n",freq);
+        return FM_SUCCESS;
+    }
+}
+
+int  FmIoctlsInterface :: get_lowerband_limit
+(
+    UINT fd, ULINT &freq
+)
+{
+    int ret;
+    struct v4l2_tuner tuner;
+
+    tuner.index = 0;
+    ret = ioctl(fd, VIDIOC_G_TUNER, &tuner);
+    if(ret != IOCTL_SUCC) {
+        return FM_FAILURE;
+    }else {
+        freq = (tuner.rangelow / TUNE_MULT);
+        ALOGE("low freq: %d\n",freq);
+        return FM_SUCCESS;
+    }
+}
+
+int  FmIoctlsInterface :: set_audio_mode
+(
+    UINT fd, enum AUDIO_MODE mode
+)
+{
+    int ret;
+    struct v4l2_tuner tuner;
+
+    tuner.index = 0;
+    ret = ioctl(fd, VIDIOC_G_TUNER, &tuner);
+    if(ret != IOCTL_SUCC) {
+        return FM_FAILURE;
+    }else {
+        tuner.audmode = mode;
+        ret = ioctl(fd, VIDIOC_S_TUNER, &tuner);
+        if(ret != IOCTL_SUCC) {
+            return FM_FAILURE;
+        }else {
+            return FM_SUCCESS;
+        }
+    }
+}
+
+int  FmIoctlsInterface :: get_buffer
+(
+     UINT fd, char *buff, UINT len, UINT index
+)
+{
+    int ret;
+    struct v4l2_buffer v4l2_buf;
+
+    if((len < STD_BUF_SIZE) || (buff == NULL)) {
+        return FM_FAILURE;
+    }else {
+        memset(&v4l2_buf, 0, sizeof(v4l2_buf));
+        v4l2_buf.index = index;
+        v4l2_buf.type = V4L2_BUF_TYPE_PRIVATE;
+        v4l2_buf.length = STD_BUF_SIZE;
+        v4l2_buf.m.userptr = (ULINT)buff;
+        ret = ioctl(fd, VIDIOC_DQBUF, &v4l2_buf);
+        if(ret != IOCTL_SUCC) {
+            return FM_FAILURE;
+        }else {
+            return v4l2_buf.bytesused;
+        }
+    }
+}
+
+int FmIoctlsInterface :: set_ext_control
+(
+    UINT fd,
+    struct v4l2_ext_controls *v4l2_ctls
+)
+{
+    int ret;
+
+    ret = ioctl(fd, VIDIOC_S_EXT_CTRLS, v4l2_ctls);
+
+    if(ret != IOCTL_SUCC) {
+       return FM_FAILURE;
+    }else {
+       return FM_SUCCESS;
+    }
+}
+
diff --git a/libfm_jni/FmIoctlsInterface.h b/libfm_jni/FmIoctlsInterface.h
new file mode 100644
index 0000000..e2ae601
--- /dev/null
+++ b/libfm_jni/FmIoctlsInterface.h
@@ -0,0 +1,57 @@
+/*
+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 __FM_IOCTL_INTERFACE_H__
+#define __FM_IOCTL_INTERFACE_H__
+
+#include "FM_Const.h"
+
+#include <linux/videodev2.h>
+
+class FmIoctlsInterface
+{
+    public:
+        static int start_fm_patch_dl(UINT fd);
+        static int close_fm_patch_dl(void);
+        static int get_cur_freq(UINT fd, long &freq);
+        static int set_freq(UINT fd, ULINT freq);
+        static int set_control(UINT fd, UINT id, int val);
+        static int set_calibration(UINT fd);
+        static int get_control(UINT fd, UINT id, long &val);
+        static int start_search(UINT fd, UINT dir);
+        static int set_band(UINT fd, ULINT low, ULINT high);
+        static int get_upperband_limit(UINT fd, ULINT &freq);
+        static int get_lowerband_limit(UINT fd, ULINT &freq);
+        static int set_audio_mode(UINT fd, enum AUDIO_MODE mode);
+        static int get_buffer(UINT fd, char *buff, UINT len, UINT index);
+        static int get_rmssi(UINT fd, long &rmssi);
+        static int set_ext_control(UINT fd, struct v4l2_ext_controls *v4l2_ctls);
+};
+
+#endif //__FM_IOCTL_INTERFACE_H__
diff --git a/libfm_jni/FmPerformanceParams.cpp b/libfm_jni/FmPerformanceParams.cpp
new file mode 100644
index 0000000..dc22432
--- /dev/null
+++ b/libfm_jni/FmPerformanceParams.cpp
@@ -0,0 +1,545 @@
+/*
+Copyright (c) 2015, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+    * Neither the name of The Linux Foundation nor the names of its
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <cstdio>
+#include <utils/Log.h>
+#include "FmPerformanceParams.h"
+#include "FmIoctlsInterface.h"
+#include <linux/videodev2.h>
+
+signed char FmPerformanceParams :: SetBand
+(
+   UINT fd, unsigned char band
+)
+{
+   signed char ret = FM_FAILURE;
+
+   switch(band) {
+   case BAND_87500_108000:
+       ret = FmIoctlsInterface::set_band(fd,
+                               87500, 108000);
+       break;
+   case BAND_76000_108000:
+       ret = FmIoctlsInterface::set_band(fd,
+                               76000, 108000);
+       break;
+   case BAND_76000_90000:
+       ret = FmIoctlsInterface::set_band(fd,
+                               76000, 90000);
+       break;
+   default:
+       ALOGE("Band type: %ld is invalid\n", band);
+       ret = FM_FAILURE;
+       break;
+   }
+   return ret;
+}
+
+signed char FmPerformanceParams :: SetEmphsis
+(
+   UINT fd, unsigned char emph
+)
+{
+   signed char ret = FM_FAILURE;
+
+   switch(emph) {
+   case DE_EMP75:
+   case DE_EMP50:
+       ret = FmIoctlsInterface::set_control(fd,
+                       V4L2_CID_PRV_EMPHASIS, emph);
+       break;
+   default:
+       ALOGE("invalid de-emphasis value\n");
+       ret = FM_FAILURE;
+       break;
+   }
+   return ret;
+}
+
+signed char FmPerformanceParams :: SetChannelSpacing
+(
+   UINT fd, unsigned char spacing
+)
+{
+   signed char ret = FM_FAILURE;
+
+   ret = FmIoctlsInterface::set_control(fd,
+                               V4L2_CID_PRV_CHAN_SPACING, spacing);
+   return ret;
+}
+
+signed char FmPerformanceParams :: SetAfRmssiTh
+(
+   UINT fd, unsigned short th
+)
+{
+   signed char ret = FM_FAILURE;
+
+   ret = FmIoctlsInterface::set_control(fd,
+                V4L2_CID_PRV_AF_RMSSI_TH, th);
+
+   return ret;
+}
+
+signed char FmPerformanceParams :: SetAfRmssiSamplesCnt
+(
+   UINT fd, unsigned char cnt
+)
+{
+   signed char ret = FM_FAILURE;
+
+   ret = FmIoctlsInterface::set_control(fd,
+                V4L2_CID_PRV_AF_RMSSI_SAMPLES, cnt);
+
+   return ret;
+}
+
+signed char FmPerformanceParams :: SetGoodChannelRmssiTh
+(
+   UINT fd, signed char th
+)
+{
+   signed char ret = FM_FAILURE;
+
+   ret = FmIoctlsInterface::set_control(fd,
+                V4L2_CID_PRV_GOOD_CH_RMSSI_TH, th);
+
+   return ret;
+}
+
+signed char FmPerformanceParams :: SetSrchAlgoType
+(
+   UINT fd, unsigned char algo
+)
+{
+   signed char ret = FM_FAILURE;
+
+   ret = FmIoctlsInterface::set_control(fd,
+                V4L2_CID_PRV_SRCHALGOTYPE, algo);
+
+   return ret;
+}
+
+signed char FmPerformanceParams :: SetSinrFirstStage
+(
+   UINT fd, signed char th
+)
+{
+   signed char ret = FM_FAILURE;
+
+   ret = FmIoctlsInterface::set_control(fd,
+                V4L2_CID_PRV_SINRFIRSTSTAGE, th);
+
+   return ret;
+}
+
+signed char FmPerformanceParams :: SetRmssiFirstStage
+(
+   UINT fd, signed char th
+)
+{
+   signed char ret = FM_FAILURE;
+
+   ret = FmIoctlsInterface::set_control(fd,
+                V4L2_CID_PRV_RMSSIFIRSTSTAGE, th);
+
+   return ret;
+}
+
+signed char FmPerformanceParams :: SetCf0Th12
+(
+   UINT fd, int th
+)
+{
+   signed char ret = FM_FAILURE;
+
+   ret = FmIoctlsInterface::set_control(fd,
+                V4L2_CID_PRV_CF0TH12, th);
+   return ret;
+}
+
+signed char FmPerformanceParams :: SetSinrSamplesCnt
+(
+   UINT fd, unsigned char cnt
+)
+{
+   signed char ret = FM_FAILURE;
+
+   ret = FmIoctlsInterface::set_control(fd,
+                V4L2_CID_PRV_SINR_SAMPLES, cnt);
+
+   return ret;
+}
+
+signed char FmPerformanceParams :: SetIntfLowTh
+(
+   UINT fd, unsigned char th
+)
+{
+   signed char ret = FM_FAILURE;
+
+   ret = FmIoctlsInterface::set_control(fd,
+                V4L2_CID_PRV_ON_CHANNEL_THRESHOLD, th);
+
+   return ret;
+}
+
+signed char FmPerformanceParams :: SetIntfHighTh
+(
+   UINT fd, unsigned char th
+)
+{
+   signed char ret = FM_FAILURE;
+
+   ret = FmIoctlsInterface::set_control(fd,
+                V4L2_CID_PRV_OFF_CHANNEL_THRESHOLD, th);
+   return ret;
+}
+
+signed char FmPerformanceParams :: SetSinrFinalStage
+(
+   UINT fd, signed char th
+)
+{
+   signed char ret = FM_FAILURE;
+
+   ret = FmIoctlsInterface::set_control(fd,
+                V4L2_CID_PRV_SINR_THRESHOLD, th);
+
+   return ret;
+}
+
+signed char FmPerformanceParams :: GetAfRmssiTh
+(
+   UINT fd, unsigned short &th
+)
+{
+   long int af_rmssi_th;
+   signed char ret = FM_FAILURE;
+
+   ret = FmIoctlsInterface::get_control(fd,
+                V4L2_CID_PRV_AF_RMSSI_TH, af_rmssi_th);
+   if(ret == FM_SUCCESS) {
+      th = af_rmssi_th;
+   }
+   return ret;
+}
+
+signed char FmPerformanceParams :: GetAfRmssiSamplesCnt
+(
+   UINT fd, unsigned char &cnt
+)
+{
+   long int af_samples_cnt;
+   signed char ret = FM_FAILURE;
+
+   ret = FmIoctlsInterface::get_control(fd,
+                V4L2_CID_PRV_AF_RMSSI_SAMPLES, af_samples_cnt);
+   if(ret == FM_SUCCESS) {
+      cnt = af_samples_cnt;
+   }
+   return ret;
+}
+
+signed char FmPerformanceParams :: GetGoodChannelRmssiTh
+(
+   UINT fd, signed char &th
+)
+{
+   long int gd_chan_rmssi_th;
+   signed char ret = FM_FAILURE;
+
+   ret = FmIoctlsInterface::get_control(fd,
+                V4L2_CID_PRV_GOOD_CH_RMSSI_TH, gd_chan_rmssi_th);
+   if(ret == FM_SUCCESS) {
+      th = gd_chan_rmssi_th;
+   }
+   return ret;
+}
+
+signed char FmPerformanceParams :: GetSrchAlgoType
+(
+   UINT fd, unsigned char &algo
+)
+{
+   long int srch_algo_type;
+   signed char ret = FM_FAILURE;
+
+   ret = FmIoctlsInterface::get_control(fd,
+                V4L2_CID_PRV_SRCHALGOTYPE, srch_algo_type);
+   if(ret == FM_SUCCESS) {
+      algo = srch_algo_type;
+   }
+   return ret;
+}
+
+signed char FmPerformanceParams :: GetSinrFirstStage
+(
+   UINT fd, signed char &th
+)
+{
+   long int sinr_first_stage;
+   signed char ret = FM_FAILURE;
+
+   ret = FmIoctlsInterface::get_control(fd,
+                V4L2_CID_PRV_SINRFIRSTSTAGE, sinr_first_stage);
+   if(ret == FM_SUCCESS) {
+      th = sinr_first_stage;
+   }
+   return ret;
+}
+
+signed char FmPerformanceParams :: GetRmssiFirstStage
+(
+   UINT fd, signed char &th
+)
+{
+   long int rmssi_first_stage;
+   signed char ret = FM_FAILURE;
+
+   ret = FmIoctlsInterface::get_control(fd,
+                V4L2_CID_PRV_RMSSIFIRSTSTAGE, rmssi_first_stage);
+   if(ret == FM_SUCCESS) {
+      th = rmssi_first_stage;
+   }
+   return ret;
+}
+
+signed char FmPerformanceParams :: GetCf0Th12
+(
+   UINT fd, int &th
+)
+{
+   long int cf0th12;
+   signed char ret = FM_FAILURE;
+
+   ret = FmIoctlsInterface::get_control(fd,
+                V4L2_CID_PRV_CF0TH12, cf0th12);
+   if(ret == FM_SUCCESS) {
+      th = cf0th12;
+   }
+   return ret;
+}
+
+signed char FmPerformanceParams :: GetSinrSamplesCnt
+(
+   UINT fd, unsigned char &cnt
+)
+{
+   long int sinr_samples_cnt;
+   signed char ret = FM_FAILURE;
+
+   ret = FmIoctlsInterface::get_control(fd,
+                V4L2_CID_PRV_SINR_SAMPLES, sinr_samples_cnt);
+   if(ret == FM_SUCCESS) {
+      cnt = sinr_samples_cnt;
+   }
+   return ret;
+}
+
+signed char FmPerformanceParams :: GetIntfLowTh
+(
+   UINT fd, unsigned char &th
+)
+{
+   long int intf_low_th;
+   signed char ret = FM_FAILURE;
+
+   ret = FmIoctlsInterface::get_control(fd,
+               V4L2_CID_PRV_ON_CHANNEL_THRESHOLD, intf_low_th);
+   if(ret == FM_SUCCESS) {
+      th = intf_low_th;
+   }
+   return ret;
+}
+
+signed char FmPerformanceParams :: GetIntfHighTh
+(
+   UINT fd, unsigned char &th
+)
+{
+   long int intf_high_th;
+   signed char ret = FM_FAILURE;
+
+   ret = FmIoctlsInterface::get_control(fd,
+               V4L2_CID_PRV_OFF_CHANNEL_THRESHOLD, intf_high_th);
+   if(ret == FM_SUCCESS) {
+      th = intf_high_th;
+   }
+   return ret;
+}
+
+signed char FmPerformanceParams :: GetIntfDet
+(
+   UINT fd, unsigned char &th
+)
+{
+   long int int_det;
+   signed char ret = FM_FAILURE;
+
+   ret = FmIoctlsInterface::get_control(fd,
+               V4L2_CID_PRV_INTDET, int_det);
+   if(ret == FM_SUCCESS) {
+      th = int_det;
+   }
+   return ret;
+}
+signed char FmPerformanceParams :: GetSinrFinalStage
+(
+   UINT fd, signed char &th
+)
+{
+   signed char ret = FM_FAILURE;
+   long int sinr;
+
+   ret = FmIoctlsInterface::get_control(fd,
+                V4L2_CID_PRV_SINR_THRESHOLD, sinr);
+
+   if(ret == FM_SUCCESS) {
+      th = sinr;
+   }
+   return ret;
+}
+
+signed char FmPerformanceParams :: SetHybridSrchList
+(
+   UINT fd,
+   unsigned int *freqs,
+   signed char *sinrs,
+   unsigned int n
+)
+{
+   struct v4l2_ext_control ext_ctl;
+   struct v4l2_ext_controls v4l2_ctls;
+   unsigned int freq;
+   signed char sinr;
+   unsigned int size = 0;
+   char *data = NULL;
+   signed char ret = FM_FAILURE;
+
+   if(n <= 0) {
+      return ret;
+   }
+   data = new char[(n * HYBRID_SRCH_DATA_LEN + HYBRID_SRCH_DATA_INDEX)];
+
+   if(data != NULL) {
+      data[size++] = HYBRID_SRCH_MODE;
+      data[size++] = ((n * HYBRID_SRCH_DATA_LEN) + 1);
+      data[size++] = n;
+      while((size < (n * HYBRID_SRCH_DATA_LEN + 2)) && (freqs != NULL)
+            && (sinrs != NULL)) {
+            freq = (*freqs - STARTING_FREQ) / FREQ_MULTIPLEX;
+            data[size++] = (freq & 0xff);
+            data[size++] = ((freq >> 8) & 0xff);
+            data[size++] = *sinrs;
+            freqs++;
+            sinrs++;
+      }
+      if(size == (n * HYBRID_SRCH_DATA_LEN + HYBRID_SRCH_DATA_INDEX)) {
+         ext_ctl.id = V4L2_CID_PRV_IRIS_WRITE_DEFAULT;
+         ext_ctl.string = data;
+         ext_ctl.size = size;
+         v4l2_ctls.ctrl_class = V4L2_CTRL_CLASS_USER;
+         v4l2_ctls.count = 1;
+         v4l2_ctls.controls  = &ext_ctl;
+         ret =  FmIoctlsInterface::set_ext_control(fd, &v4l2_ctls);
+         if(ret == FM_SUCCESS) {
+            ALOGE("hybrid srch list sent successfully\n");
+         }else {
+            ALOGE("hybrid srch list setting failed\n");
+         }
+      }
+   }
+
+   delete []data;
+
+   return ret;
+}
+
+signed char FmPerformanceParams :: SetBlendSinr
+(
+   UINT fd, signed char bsinr
+)
+{
+   signed char ret = FM_FAILURE;
+
+   if ((bsinr >= MIN_BLEND_SINRHI) &&
+       (bsinr <= MAX_BLEND_SINRHI))
+        ret = FmIoctlsInterface::set_control(fd,
+                   V4L2_CID_PRV_IRIS_BLEND_SINRHI, bsinr);
+
+   return ret;
+}
+
+signed char FmPerformanceParams :: GetBlendSinr
+(
+   UINT fd, signed char &bsinr
+)
+{
+   long int blend_sinr_hi;
+   signed char ret = FM_FAILURE;
+
+   ret = FmIoctlsInterface::get_control(fd,
+                V4L2_CID_PRV_IRIS_BLEND_SINRHI, blend_sinr_hi);
+   if(ret == FM_SUCCESS) {
+      bsinr = blend_sinr_hi;
+   }
+   return ret;
+}
+
+signed char FmPerformanceParams :: SetBlendRmssi
+(
+   UINT fd, signed char brmssi
+)
+{
+   signed char ret = FM_FAILURE;
+
+    if ((brmssi >= MIN_BLEND_RMSSIHI) &&
+        (brmssi <= MAX_BLEND_RMSSIHI))
+         ret = FmIoctlsInterface::set_control(fd,
+                   V4L2_CID_PRV_IRIS_BLEND_RMSSIHI, brmssi);
+
+   return ret;
+}
+
+signed char FmPerformanceParams :: GetBlendRmssi
+(
+   UINT fd, signed char &brmssi
+)
+{
+   long int blend_rmssi_hi;
+   signed char ret = FM_FAILURE;
+
+   ret = FmIoctlsInterface::get_control(fd,
+                V4L2_CID_PRV_IRIS_BLEND_RMSSIHI, blend_rmssi_hi);
+   if(ret == FM_SUCCESS) {
+      brmssi = blend_rmssi_hi;
+   }
+   return ret;
+}
diff --git a/libfm_jni/FmPerformanceParams.h b/libfm_jni/FmPerformanceParams.h
new file mode 100644
index 0000000..88e53cc
--- /dev/null
+++ b/libfm_jni/FmPerformanceParams.h
@@ -0,0 +1,78 @@
+/*
+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 __FM_PERFORMANCE_PARAMS_H__
+#define __FM_PERFORMANCE_PARAMS_H__
+
+#include "FM_Const.h"
+
+#define MIN_BLEND_SINRHI -128
+#define MAX_BLEND_SINRHI  127
+#define MIN_BLEND_RMSSIHI -128
+#define MAX_BLEND_RMSSIHI  127
+
+class FmPerformanceParams
+{
+      private:
+      public:
+          signed char SetBand(UINT fd, unsigned char band);
+          signed char SetEmphsis(UINT fd, unsigned char emph);
+          signed char SetChannelSpacing(UINT fd, unsigned char spacing);
+          signed char SetAfRmssiTh(UINT fd, unsigned short th);
+          signed char SetAfRmssiSamplesCnt(UINT fd, unsigned char cnt);
+          signed char SetGoodChannelRmssiTh(UINT fd, signed char th);
+          signed char SetSrchAlgoType(UINT fd, unsigned char algo);
+          signed char SetSinrFirstStage(UINT fd, signed char th);
+          signed char SetRmssiFirstStage(UINT fd, signed char th);
+          signed char SetCf0Th12(UINT fd, int th);
+          signed char SetSinrSamplesCnt(UINT fd, unsigned char cnt);
+          signed char SetIntfLowTh(UINT fd, unsigned char th);
+          signed char SetIntfHighTh(UINT fd, unsigned char th);
+          signed char SetSinrFinalStage(UINT fd, signed char th);
+          signed char SetHybridSrchList(UINT fd, unsigned int *freqs, signed char *sinrs, unsigned int n);
+          signed char SetBlendSinr(UINT fd, signed char bsinr);
+          signed char SetBlendRmssi(UINT fd, signed char brmssi);
+
+          signed char GetAfRmssiTh(UINT fd, unsigned short &th);
+          signed char GetAfRmssiSamplesCnt(UINT fd, unsigned char &cnt);
+          signed char GetGoodChannelRmssiTh(UINT fd, signed char &th);
+          signed char GetSrchAlgoType(UINT fd, unsigned char &algo);
+          signed char GetSinrFirstStage(UINT fd, signed char &th);
+          signed char GetRmssiFirstStage(UINT fd, signed char &th);
+          signed char GetCf0Th12(UINT fd, int &th);
+          signed char GetSinrSamplesCnt(UINT fd, unsigned char &cnt);
+          signed char GetIntfLowTh(UINT fd, unsigned char &th);
+          signed char GetIntfHighTh(UINT fd, unsigned char &th);
+          signed char GetIntfDet(UINT fd, unsigned char &th);
+          signed char GetSinrFinalStage(UINT fd, signed char &th);
+          signed char GetBlendSinr(UINT fd, signed char &bsinr);
+          signed char GetBlendRmssi(UINT fd, signed char &brmssi);
+};
+
+#endif //__FM_PERFORMANCE_PARAMS_H__
diff --git a/libfm_jni/FmRadioController.cpp b/libfm_jni/FmRadioController.cpp
new file mode 100644
index 0000000..55bcef4
--- /dev/null
+++ b/libfm_jni/FmRadioController.cpp
@@ -0,0 +1,1434 @@
+/*
+Copyright (c) 2015, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+    * Neither the name of The Linux Foundation nor the names of its
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <fcntl.h>
+#include <utils/Log.h>
+#include <cutils/properties.h>
+#include <sys/ioctl.h>
+#include <math.h>
+#include "FmRadioController.h"
+#include "FmIoctlsInterface.h"
+#include "ConfigFmThs.h"
+#include <linux/videodev2.h>
+
+//Reset all variables to default value
+static FmIoctlsInterface * FmIoct;
+FmRadioController :: FmRadioController
+(
+)
+{
+    cur_fm_state = FM_OFF;
+    prev_freq = -1;
+    seek_scan_canceled = false;
+    af_enabled = 0;
+    rds_enabled = 0;
+    event_listener_canceled = false;
+    is_rds_support = false;
+    is_ps_event_received = false;
+    is_rt_event_received = false;
+    is_af_jump_received = false;
+    mutex_fm_state = PTHREAD_MUTEX_INITIALIZER;
+    mutex_seek_compl_cond = PTHREAD_MUTEX_INITIALIZER;
+    mutex_scan_compl_cond = PTHREAD_MUTEX_INITIALIZER;
+    mutex_tune_compl_cond = PTHREAD_MUTEX_INITIALIZER;
+    mutex_turn_on_cond = PTHREAD_MUTEX_INITIALIZER;
+    turn_on_cond = PTHREAD_COND_INITIALIZER;
+    seek_compl_cond = PTHREAD_COND_INITIALIZER;
+    scan_compl_cond = PTHREAD_COND_INITIALIZER;
+    tune_compl_cond = PTHREAD_COND_INITIALIZER;
+    event_listener_thread = 0;
+    fd_driver = -1;
+    FmIoct = new FmIoctlsInterface();
+}
+
+/* Turn off FM */
+FmRadioController :: ~FmRadioController
+(
+)
+{
+    if((cur_fm_state != FM_OFF)) {
+        Stop_Scan_Seek();
+        set_fm_state(FM_OFF_IN_PROGRESS);
+        FmIoctlsInterface::set_control(fd_driver,
+                        V4L2_CID_PRV_STATE, FM_DEV_NONE);
+    }
+    if(event_listener_thread != 0) {
+        event_listener_canceled = true;
+        pthread_join(event_listener_thread, NULL);
+    }
+}
+
+int FmRadioController ::open_dev()
+{
+    int ret = FM_SUCCESS;
+
+    fd_driver = open(FM_DEVICE_PATH, O_RDONLY, O_NONBLOCK);
+
+    if (fd_driver < 0) {
+        ALOGE("%s failed, [fd=%d] %s\n", __func__, fd_driver, FM_DEVICE_PATH);
+        return FM_FAILURE;
+    }
+
+    ALOGD("%s, [fd=%d] \n", __func__, fd_driver);
+    return ret;
+}
+
+int FmRadioController ::close_dev()
+{
+    int ret = 0;
+
+    if (fd_driver > 0) {
+        close(fd_driver);
+        fd_driver = -1;
+    }
+    ALOGD("%s, [fd=%d] [ret=%d]\n", __func__, fd_driver, ret);
+    return ret;
+}
+
+struct timespec FmRadioController :: set_time_out
+(
+    int secs
+)
+{
+    struct timespec ts;
+    struct timeval tp;
+
+    gettimeofday(&tp, NULL);
+    ts.tv_sec = tp.tv_sec;
+    ts.tv_nsec = tp.tv_usec * 1000;
+    ts.tv_sec += secs;
+
+    return ts;
+}
+
+//Get current tuned frequency
+//Return -1 if failed to get freq
+long FmRadioController :: GetChannel
+(
+    void
+)
+{
+    long freq = -1;
+    int ret;
+
+    if((cur_fm_state != FM_OFF) &&
+       (cur_fm_state != FM_ON_IN_PROGRESS)) {
+       ret = FmIoctlsInterface::get_cur_freq(fd_driver, freq);
+       if(ret == FM_SUCCESS) {
+          ALOGI("FM get freq is successfull, freq is: %ld\n", freq);
+       }else {
+          ALOGE("FM get frequency failed, freq is: %ld\n", freq);
+       }
+    }else {
+       ALOGE("FM get freq is not valid in current state\n");
+    }
+    return freq;
+}
+
+int FmRadioController ::Pwr_Up(int freq)
+{
+    int ret = FM_SUCCESS;
+    struct timespec ts;
+    ConfigFmThs thsObj;
+
+    ALOGI("%s,[freq=%d]\n", __func__, freq);
+    if (fd_driver < 0) {
+        ret = open_dev();
+        if (ret != FM_SUCCESS) {
+            ALOGE("Dev open failed\n");
+            return FM_FAILURE;
+        }
+    }
+
+    if (cur_fm_state == FM_OFF) {
+        ALOGE("cur_fm_state = %d\n",cur_fm_state);
+        ret = FmIoctlsInterface::start_fm_patch_dl(fd_driver);
+        if (ret != FM_SUCCESS) {
+            ALOGE("FM patch downloader failed: %d\n", ret);
+            close_dev();
+            set_fm_state(FM_OFF);
+            return FM_FAILURE;
+        }
+        if (event_listener_thread == 0) {
+            ret = pthread_create(&event_listener_thread, NULL,
+                                              handle_events, this);
+            if (ret == 0) {
+                ALOGI("Lock the mutex for FM turn on cond\n");
+                pthread_mutex_lock(&mutex_turn_on_cond);
+                ts = set_time_out(READY_EVENT_TIMEOUT);
+                ret = FmIoctlsInterface::set_control(fd_driver,
+                                             V4L2_CID_PRV_STATE, FM_RX);
+                if (ret == FM_SUCCESS) {
+                    ALOGI("Waiting for timedout or FM on\n");
+                    pthread_cond_timedwait(&turn_on_cond,
+                                       &mutex_turn_on_cond, &ts);
+                    ALOGI("Unlocked mutex & timedout or condition satisfied\n");
+                    pthread_mutex_unlock(&mutex_turn_on_cond);
+                    if (cur_fm_state == FM_ON) {//after READY event
+                        ret = SetBand(BAND_87500_108000);
+                        if (ret != FM_SUCCESS) {
+                            ALOGE("set band failed\n");
+                            ret = FM_FAILURE;
+                            goto exit;
+                        }
+                        ret = SetChannelSpacing(CHAN_SPACE_100);
+                        if (ret != FM_SUCCESS) {
+                            ALOGE("set channel spacing failed\n");
+                            ret = FM_FAILURE;
+                            goto exit;
+                        }
+                        ret = SetDeConstant(DE_EMP50);
+                        if (ret != FM_SUCCESS) {
+                            ALOGE("set Emphasis failed\n");
+                            ret = FM_FAILURE;
+                            goto exit;
+                        }
+                        thsObj.SetRxSearchAfThs(FM_PERFORMANCE_PARAMS, fd_driver);
+                        SetStereo();
+                        ret = TuneChannel(freq);
+                        if (ret != FM_SUCCESS) {
+                            ALOGI("FM set freq command failed\n");
+                            ret = FM_FAILURE;
+                            goto exit;
+                        }
+                        return FM_SUCCESS;
+                    } else { //if time out
+                        ret = FM_FAILURE;
+                        goto exit;
+                    }
+                } else {
+                    ALOGE("Set FM on control failed\n");
+                    pthread_mutex_unlock(&mutex_turn_on_cond);
+                    ALOGI("Unlocked the FM on cond mutex\n");
+                    ret = FM_FAILURE;
+                    goto close_fd;
+                }
+            } else {
+                ALOGE("FM event listener thread failed: %d\n", ret);
+                set_fm_state(FM_OFF);
+                return FM_FAILURE;
+            }
+        } else {
+            ALOGE("FM event listener threadi existed\n");
+            return FM_SUCCESS;
+        }
+    } else if(cur_fm_state != FM_ON_IN_PROGRESS) {
+        return FM_SUCCESS;
+    } else {
+        return FM_FAILURE;
+    }
+
+exit:
+    FmIoctlsInterface::set_control(fd_driver,
+                                     V4L2_CID_PRV_STATE, FM_DEV_NONE);
+close_fd:
+    event_listener_canceled = true;
+    pthread_join(event_listener_thread, NULL);
+    close(fd_driver);
+    fd_driver = -1;
+    set_fm_state(FM_OFF);
+
+    ALOGD("%s, [ret=%d]\n", __func__, ret);
+    return ret;
+}
+
+int FmRadioController ::Pwr_Down()
+{
+    int ret = 0;
+
+    if((cur_fm_state != FM_OFF)) {
+        Stop_Scan_Seek();
+        set_fm_state(FM_OFF_IN_PROGRESS);
+        FmIoctlsInterface::set_control(fd_driver,
+                        V4L2_CID_PRV_STATE, FM_DEV_NONE);
+    }
+    if(event_listener_thread != 0) {
+        ALOGD("%s, event_listener_thread canceeled\n", __func__);
+        event_listener_canceled = true;
+        pthread_join(event_listener_thread, NULL);
+    }
+    ALOGD("%s, [ret=%d]\n", __func__, ret);
+    return ret;
+}
+//Tune to a Freq
+//Return FM_SUCCESS on success FM_FAILURE
+//on failure
+int FmRadioController :: TuneChannel
+(
+    long freq
+)
+{
+    int ret = FM_SUCCESS;
+    struct timespec ts;
+
+    if((cur_fm_state == FM_ON) &&
+        (freq > 0)) {
+        set_fm_state(FM_TUNE_IN_PROGRESS);
+        ret = FmIoctlsInterface::set_freq(fd_driver,
+                                             freq);
+        if(ret == FM_SUCCESS) {
+           ALOGI("FM set frequency command set successfully\n");
+           pthread_mutex_lock(&mutex_tune_compl_cond);
+           ts = set_time_out(TUNE_EVENT_TIMEOUT);
+           ret = pthread_cond_timedwait(&tune_compl_cond, &mutex_tune_compl_cond, &ts);
+           pthread_mutex_unlock(&mutex_tune_compl_cond);
+        }else {
+           if((cur_fm_state != FM_OFF)) {
+              set_fm_state(FM_ON);
+           }
+           ALOGE("FM set freq command failed\n");
+        }
+    }else {
+        ALOGE("Fm is not in proper state for tuning to a freq\n");
+        ret = FM_FAILURE;
+    }
+    return ret;
+}
+
+int FmRadioController :: Seek(int dir)
+{
+    int ret = 0;
+    int freq = -1;
+    struct timespec ts;
+
+    if (cur_fm_state != FM_ON) {
+        ALOGE("%s error Fm state: %d\n", __func__,cur_fm_state);
+        return FM_FAILURE;
+    }
+
+    ALOGI("FM seek started\n");
+    set_fm_state(SEEK_IN_PROGRESS);
+    ret = FmIoctlsInterface::set_control(fd_driver,
+                                  V4L2_CID_PRV_SRCHMODE, SEEK_MODE);
+    if (ret != FM_SUCCESS) {
+        set_fm_state(FM_ON);
+        return FM_FAILURE;
+    }
+
+    ret = FmIoctlsInterface::set_control(fd_driver,
+                           V4L2_CID_PRV_SCANDWELL, SEEK_DWELL_TIME);
+    if (ret != FM_SUCCESS) {
+        set_fm_state(FM_ON);
+        return FM_FAILURE;
+    }
+
+    if (dir == 1) {
+        ret = FmIoctlsInterface::start_search(fd_driver,
+                                                     SEARCH_UP);
+    } else {
+        ret = FmIoctlsInterface::start_search(fd_driver,
+                                            SEARCH_DOWN);
+    }
+
+    if (ret != FM_SUCCESS) {
+        set_fm_state(FM_ON);
+        return FM_FAILURE;
+    }
+    pthread_mutex_lock(&mutex_seek_compl_cond);
+    ts = set_time_out(SEEK_COMPL_TIMEOUT);
+    ret = pthread_cond_timedwait(&seek_compl_cond, &mutex_seek_compl_cond, &ts);
+    pthread_mutex_unlock(&mutex_seek_compl_cond);
+    if ((cur_fm_state != SEEK_IN_PROGRESS) && !seek_scan_canceled) {
+        ALOGI("Seek completed without timeout\n");
+        freq = GetChannel();
+    }
+    seek_scan_canceled = false;
+    return freq;
+}
+
+bool FmRadioController ::IsRds_support
+(
+    void
+)
+{
+   is_rds_support = true;
+   ALOGI("is_rds_support: \n", is_rds_support);
+   return is_rds_support;
+}
+
+//HardMute both audio channels
+int FmRadioController ::MuteOn()
+{
+    int ret;
+
+    ALOGE("cur_fm_state = %d\n", cur_fm_state);
+    if((cur_fm_state != FM_OFF) &&
+       (cur_fm_state != FM_ON_IN_PROGRESS)) {
+       ret = FmIoctlsInterface::set_control(fd_driver,
+                       V4L2_CID_AUDIO_MUTE, MUTE_L_R_CHAN);
+       ALOGE("CMD executed mute\n");
+    }else {
+       ret = FM_FAILURE;
+    }
+    return ret;
+}
+
+//Unmute both audio channel
+int FmRadioController ::MuteOff()
+{
+    int ret;
+
+    ALOGE("cur_fm_state = %d\n", cur_fm_state);
+    if((cur_fm_state != FM_OFF) &&
+       (cur_fm_state != FM_ON_IN_PROGRESS)) {
+        ret = FmIoctlsInterface::set_control(fd_driver,
+                      V4L2_CID_AUDIO_MUTE, UNMUTE_L_R_CHAN);
+        ALOGE("CMD executed for unmute\n");
+    }else {
+        ret = FM_FAILURE;
+    }
+    return ret;
+}
+
+//
+int FmRadioController ::SetSoftMute(bool mode)
+{
+    int ret;
+
+    if((cur_fm_state != FM_OFF) &&
+       (cur_fm_state != FM_ON_IN_PROGRESS)) {
+        ret = FmIoctlsInterface::set_control(fd_driver,
+                             V4L2_CID_PRV_SOFT_MUTE, mode);
+    }else {
+        ret = FM_FAILURE;
+    }
+    return ret;
+}
+
+int FmRadioController :: Set_mute(bool mute)
+{
+    int ret = 0;
+
+    if (mute) {
+        ret = MuteOn();
+    } else {
+        ret = MuteOff();
+    }
+
+    if (ret)
+        ALOGE("%s failed, %d\n", __func__, ret);
+    ALOGD("%s, [mute=%d] [ret=%d]\n", __func__, mute, ret);
+    return ret;
+}
+
+int FmRadioController :: Stop_Scan_Seek
+(
+)
+{
+   int ret;
+
+    if((cur_fm_state == SEEK_IN_PROGRESS) ||
+       (cur_fm_state == SCAN_IN_PROGRESS)) {
+        ret = FmIoctlsInterface::set_control(fd_driver,
+                                       V4L2_CID_PRV_SRCHON, 0);
+        if (ret == FM_SUCCESS) {
+            ALOGI("FM Seek cancel command set successfully\n");
+            seek_scan_canceled = true;
+        } else {
+            ALOGE("FM Seek cancel command sent failed\n");
+        }
+    } else {
+        ALOGE("FM is not in proper state for cancelling Seek operation\n");
+        ret = FM_FAILURE;
+    }
+    return ret;
+}
+
+int FmRadioController :: ReadRDS() //todo define each RDS flag
+{
+   int ret = 0;
+
+   if (is_ps_event_received)
+       ret |= RDS_EVT_PS_UPDATE;
+   if (is_rt_event_received)
+       ret |= RDS_EVT_RT_UPDATE;
+   if (is_af_jump_received)
+       ret |= RDS_EVT_AF_JUMP;
+
+   return ret;
+}
+
+int FmRadioController :: Get_ps(char *ps, int *ps_len)
+{
+    int ret = 0;
+    int len = 0;
+    char raw_rds[STD_BUF_SIZE];
+
+    ret = FmIoctlsInterface::get_buffer(fd_driver,
+                                    raw_rds, STD_BUF_SIZE, PS_IND);
+    if (ret <= 0) {
+        return FM_FAILURE;
+    } else {
+        if (raw_rds[PS_STR_NUM_IND] > 0) {
+            if (ps != NULL) {
+                for(int i = 0; i < MAX_PS_LEN; i++) {
+                    ps[i] = raw_rds[PS_DATA_OFFSET_IND + i];
+                    if (ps[i] == 0) {
+                        break;
+                    } else if((ps[len] <= LAST_CTRL_CHAR) ||
+                              (ps[len] >= FIRST_NON_PRNT_CHAR)) {
+                        ps[i] = SPACE_CHAR;
+                        continue;
+                    }
+                    len++;
+                }
+                if (len < (MAX_PS_LEN - 1)) {
+                    ps[len] = '\0';
+                    *ps_len = len + 1;
+                } else {
+                    *ps_len = len;
+                }
+                ALOGI("PS is: %s\n", ps);
+            } else {
+                return FM_FAILURE;
+            }
+        }
+    }
+    is_ps_event_received = false;
+    ALOGD("%s, [ps_len=%d]\n", __func__, *ps_len);
+    return FM_SUCCESS;
+}
+
+int FmRadioController :: Get_rt(char *rt, int *rt_len)
+{
+    int ret = 0;
+    int len = 0;
+    char raw_rds[STD_BUF_SIZE];
+
+    ret = FmIoctlsInterface::get_buffer(fd_driver,
+                               raw_rds, STD_BUF_SIZE, RT_IND);
+    if (ret <= 0) {
+        return FM_FAILURE;
+    } else {
+        if (rt != NULL) {
+            if ((raw_rds[RT_LEN_IND] > 0) &&
+                (raw_rds[RT_LEN_IND] <= MAX_RT_LEN)) {
+                for(len = 0; len < raw_rds[RT_LEN_IND]; len++) {
+                   rt[len] = raw_rds[RT_DATA_OFFSET_IND + len];
+                   ALOGI("Rt byte[%d]: %d\n", len, rt[len]);
+                   if ((rt[len] <= LAST_CTRL_CHAR) ||
+                       (rt[len] >= FIRST_NON_PRNT_CHAR)) {
+                       rt[len] = SPACE_CHAR;
+                       continue;
+                   }
+                }
+                if (len < (MAX_RT_LEN - 1)) {
+                    rt[len] = '\0';
+                    *rt_len = len + 1;
+                } else {
+                    *rt_len = len;
+                }
+                ALOGI("Rt is: %s\n", rt);
+                ALOGI("RT text A / B: %d\n", raw_rds[RT_A_B_FLAG_IND]);
+            } else {
+                return FM_FAILURE;
+            }
+        } else {
+            return FM_FAILURE;
+        }
+    }
+    is_rt_event_received = false;
+    ALOGD("%s, [rt_len=%d]\n", __func__, *rt_len);
+    return FM_SUCCESS;
+}
+
+int FmRadioController :: Get_AF_freq(uint16_t *ret_freq)
+{
+    int ret =0;
+    ULINT lowBand, highBand;
+    float real_freq = 0;
+
+    ALOGI("get_AF_freq\n");
+    ret = FmIoctlsInterface::get_lowerband_limit(fd_driver,
+                                                         lowBand);
+    if (ret != FM_SUCCESS) {
+        ALOGE("failed to get lowerband: %d\n", ret);
+        return FM_FAILURE;
+    }
+    ALOGI("lowBand = %ld\n",lowBand);
+    ret = FmIoctlsInterface::get_upperband_limit(fd_driver,
+                                                      highBand);
+    if (ret != FM_SUCCESS) {
+        ALOGE("failed to getgherband: %d\n", ret);
+        return FM_FAILURE;
+    }
+    ALOGI("highBand = %ld\n",highBand);
+    real_freq = GetChannel();
+    if ((real_freq < lowBand ) || (real_freq > highBand)) {
+        ALOGE("AF freq is not in band limits\ni");
+        return FM_FAILURE;
+    } else {
+        *ret_freq = real_freq/100;
+    }
+    is_af_jump_received = false;
+    return FM_SUCCESS;
+}
+
+//Emphasis:
+//75microsec: 0, 50 microsec: 1
+//return FM_SUCCESS on success, FM_FAILURE
+//on failure
+int FmRadioController :: SetDeConstant
+(
+    long emphasis
+)
+{
+    int ret;
+
+     ALOGE("cur_fm_state: %d, emphasis: %d\n", cur_fm_state, emphasis);
+    if(cur_fm_state == FM_ON) {
+        switch(emphasis) {
+            case DE_EMP75:
+            case DE_EMP50:
+                ret = FmIoctlsInterface::set_control(fd_driver,
+                       V4L2_CID_PRV_EMPHASIS, emphasis);
+                break;
+            default:
+                ALOGE("FM value pass for set Deconstant is invalid\n");
+                ret = FM_FAILURE;
+                break;
+        }
+    }else {
+        ALOGE("FM is not in proper state to set De constant\n");
+        ret = FM_FAILURE;
+    }
+    return ret;
+}
+
+int FmRadioController :: GetStationList
+(
+    uint16_t *scan_tbl, int *max_cnt
+)
+{
+    char srch_list[STD_BUF_SIZE];
+    int ret;
+    ULINT lowBand, highBand;
+    int station_num = 0;
+    int stationList[FM_RX_SRCHLIST_MAX_STATIONS];
+    int tmpFreqByte1=0;
+    int tmpFreqByte2=0;
+    int freq = 0;
+    float real_freq = 0;
+    int i = 0, j = 0;
+
+    ALOGI("getstationList\n");
+    ret = FmIoctlsInterface::get_lowerband_limit(fd_driver,
+                                                         lowBand);
+    if (ret != FM_SUCCESS) {
+        ALOGE("failed to get lowerband: %d\n", ret);
+        return FM_FAILURE;
+    }
+    ALOGI("lowBand = %ld\n",lowBand);
+    ret = FmIoctlsInterface::get_upperband_limit(fd_driver,
+                                                      highBand);
+    if (ret != FM_SUCCESS) {
+        ALOGE("failed to getgherband: %d\n", ret);
+        return FM_FAILURE;
+    }
+    ALOGI("highBand = %ld\n",highBand);
+    ret = FmIoctlsInterface::get_buffer(fd_driver,
+                          srch_list, STD_BUF_SIZE, STATION_LIST_IND);
+    if ((int)srch_list[0] >0) {
+        station_num = (int)srch_list[0];
+    }
+    ALOGI("station_num: %d ", station_num);
+    *max_cnt = station_num;
+    for (i=0;i<station_num;i++) {
+        freq = 0;
+        ALOGI(" Byte1 = %d", srch_list[i * NO_OF_BYTES_EACH_FREQ + 1]);
+        ALOGI(" Byte2 = %d", srch_list[i * NO_OF_BYTES_EACH_FREQ + 2]);
+        tmpFreqByte1 = srch_list[i * NO_OF_BYTES_EACH_FREQ + 1] & 0xFF;
+        tmpFreqByte2 = srch_list[i * NO_OF_BYTES_EACH_FREQ + 2] & 0xFF;
+        ALOGI(" tmpFreqByte1 = %d", tmpFreqByte1);
+        ALOGI(" tmpFreqByte2 = %d", tmpFreqByte2);
+        freq = (tmpFreqByte1 & EXTRACT_FIRST_BYTE) << 8;
+        freq |= tmpFreqByte2;
+        ALOGI(" freq: %d", freq);
+        real_freq  = (freq * FREQ_MULTIPLEX) + lowBand;
+        ALOGI(" real_freq: %d", real_freq);
+        if ( (real_freq < lowBand ) || (real_freq > highBand) ) {
+              ALOGI("Frequency out of band limits");
+        } else {
+            scan_tbl[j] = (real_freq/SRCH_DIV);
+            ALOGI(" scan_tbl: %d", scan_tbl[j]);
+            j++;
+        }
+    }
+    return FM_SUCCESS;
+}
+
+int FmRadioController ::ScanList
+(
+    uint16_t *scan_tbl, int *max_cnt
+)
+{
+    int ret;
+    struct timespec ts;
+
+    /* Check current state of FM device */
+    if (cur_fm_state == FM_ON) {
+        ALOGI("FM searchlist started\n");
+        set_fm_state(SCAN_IN_PROGRESS);
+        ret = FmIoctlsInterface::set_control(fd_driver,
+                           V4L2_CID_PRV_SRCHMODE, SRCHLIST_MODE_STRONG);
+        if (ret != FM_SUCCESS) {
+            set_fm_state(FM_ON);
+            return FM_FAILURE;
+        }
+        ret = FmIoctlsInterface::set_control(fd_driver,
+                         V4L2_CID_PRV_SRCH_CNT, FM_RX_SRCHLIST_MAX_STATIONS);
+        if (ret != FM_SUCCESS) {
+            set_fm_state(FM_ON);
+            return FM_FAILURE;
+        }
+        ret = FmIoctlsInterface::start_search(fd_driver,
+                                                     SEARCH_UP);
+        if (ret != FM_SUCCESS) {
+            set_fm_state(FM_ON);
+            return FM_FAILURE;
+        }
+        pthread_mutex_lock(&mutex_scan_compl_cond);
+        ts = set_time_out(SCAN_COMPL_TIMEOUT);
+        ALOGI("Wait for Scan Timeout or scan complete");
+        ret = pthread_cond_timedwait(&scan_compl_cond, &mutex_scan_compl_cond, &ts);
+        ALOGI("Scan complete or timedout");
+        pthread_mutex_unlock(&mutex_scan_compl_cond);
+        if (cur_fm_state == FM_ON && !seek_scan_canceled) {
+            GetStationList(scan_tbl, max_cnt);
+        } else {
+            seek_scan_canceled = false;
+            return FM_FAILURE;
+        }
+    } else {
+        ALOGI("Scanlist: not proper state %d\n",cur_fm_state );
+        return FM_FAILURE;
+    }
+    return FM_SUCCESS;
+}
+
+long FmRadioController :: GetCurrentRSSI
+(
+    void
+)
+{
+    int ret;
+    long rmssi = -129;
+
+    if((cur_fm_state != FM_OFF) &&
+       (cur_fm_state != FM_ON_IN_PROGRESS)) {
+        ret = FmIoctlsInterface::get_rmssi(fd_driver, rmssi);
+    }else {
+    }
+    return rmssi;
+}
+
+//enable, disable value to receive data of a RDS group
+//return FM_SUCCESS on success, FM_FAILURE on failure
+int FmRadioController :: SetRdsGrpProcessing
+(
+    int grps
+)
+{
+    int ret;
+    long mask;
+
+    if(cur_fm_state == FM_ON) {
+       ret = FmIoctlsInterface::get_control(fd_driver,
+                     V4L2_CID_PRV_RDSGROUP_PROC, mask);
+       if(ret != FM_SUCCESS) {
+          return ret;
+       }
+       mask &= 0xC7;
+       mask |= ((grps & 0x07) << 3);
+       ret = FmIoctlsInterface::set_control(fd_driver,
+                    V4L2_CID_PRV_RDSGROUP_PROC, (int)mask);
+    }else {
+       ret = FM_FAILURE;
+    }
+    return ret;
+}
+
+//Enable RDS data receiving
+//Enable RT, PS, AF Jump, RTPLUS, ERT etc
+int FmRadioController :: EnableRDS
+(
+    void
+)
+{
+    int ret = FM_FAILURE;
+
+    ALOGE("%s:cur_fm_state = %d\n", __func__, cur_fm_state);
+    if (cur_fm_state == FM_ON) {
+        ret = FmIoctlsInterface::set_control(fd_driver,
+                      V4L2_CID_PRV_RDSON, 1);
+        if (ret != FM_SUCCESS) {
+            ALOGE("RDS ON failed\n");
+            return ret;
+        }
+        ret = SetRdsGrpProcessing(FM_RX_RDS_GRP_RT_EBL |
+                                  FM_RX_RDS_GRP_PS_EBL |
+                                  FM_RX_RDS_GRP_AF_EBL |
+                                  FM_RX_RDS_GRP_PS_SIMPLE_EBL);
+        if (ret != FM_SUCCESS) {
+            ALOGE("Set RDS grp processing\n");
+            return ret;
+        }
+        ret = FM_SUCCESS;
+        rds_enabled = 1;
+        EnableAF();
+    } else {
+        ALOGE("%s:not in proper state cur_fm_state = %d\n", cur_fm_state);
+        return ret;
+    }
+    return ret;
+}
+
+//Disable all RDS data processing
+//RT, ERT, RT PLUS, PS
+int FmRadioController :: DisableRDS
+(
+    void
+)
+{
+    int ret = FM_FAILURE;
+
+    ALOGE("%s:cur_fm_state = %d\n", __func__, cur_fm_state);
+    if (cur_fm_state == FM_ON) {
+        ret = FmIoctlsInterface::set_control(fd_driver,
+                      V4L2_CID_PRV_RDSON, 2);
+        if (ret != FM_SUCCESS) {
+            ALOGE("Disable RDS failed\n");
+            return ret;
+        }
+        ret = FM_SUCCESS;
+        rds_enabled = 0;
+        DisableAF();
+    } else {
+        ALOGE("%s:not in proper state cur_fm_state = %d\n", cur_fm_state);
+        ret = FM_FAILURE;
+    }
+    return ret;
+}
+
+int FmRadioController :: Turn_On_Off_Rds(bool onoff)
+{
+    int ret = 0;
+
+    if (onoff) {
+        ret = EnableRDS();
+    } else {
+        ret = DisableRDS();
+    }
+
+    if (ret) {
+        ALOGE("%s, failed\n", __func__);
+    }
+    ALOGD("%s, [onoff=%d] [ret=%d]\n", __func__, onoff, ret);
+    return ret;
+}
+
+//Enables Alternate Frequency switching
+int FmRadioController :: EnableAF
+(
+    void
+)
+{
+    int ret;
+    long rdsgrps;
+
+    if(cur_fm_state == FM_ON) {
+        ret = FmIoctlsInterface::get_control(fd_driver,
+                      V4L2_CID_PRV_RDSGROUP_PROC, rdsgrps);
+        ret = FmIoctlsInterface::set_control(fd_driver,
+                      V4L2_CID_PRV_RDSON, 1);
+        if(ret == FM_SUCCESS) {
+            ret = FmIoctlsInterface::set_control(fd_driver,
+                      V4L2_CID_PRV_AF_JUMP, 1);
+            if(ret == FM_SUCCESS) {
+               af_enabled = 1;
+            }
+        } else {
+        }
+    } else {
+        ret = FM_FAILURE;
+    }
+    return ret;
+}
+
+//Disables Alternate Frequency switching
+int FmRadioController :: DisableAF
+(
+    void
+)
+{
+    int ret;
+    long rdsgrps;
+
+    if(cur_fm_state == FM_ON) {
+        ret = FmIoctlsInterface::get_control(fd_driver,
+                      V4L2_CID_PRV_RDSGROUP_PROC, rdsgrps);
+        if(ret == FM_SUCCESS) {
+            ret = FmIoctlsInterface::set_control(fd_driver,
+                      V4L2_CID_PRV_AF_JUMP, 0);
+            if(ret == FM_SUCCESS) {
+               af_enabled = 0;
+            }
+        }else {
+        }
+    }else {
+        ret = FM_FAILURE;
+    }
+    return ret;
+}
+
+//Set regional band
+int FmRadioController :: SetBand
+(
+    long band
+)
+{
+    int ret;
+
+    if(cur_fm_state == FM_ON) {
+        switch(band) {
+            case BAND_87500_108000:
+                ret = FmIoctlsInterface::set_band(fd_driver,
+                               87500, 108000);
+                break;
+            case BAND_76000_108000:
+                ret = FmIoctlsInterface::set_band(fd_driver,
+                               76000, 108000);
+                break;
+            case BAND_76000_90000:
+                ret = FmIoctlsInterface::set_band(fd_driver,
+                               76000, 90000);
+                break;
+            default:
+                ALOGE("Band type: %ld is invalid\n", band);
+                ret = FM_FAILURE;
+                break;
+        }
+    }else {
+        ALOGE("FM is not in proper state to set band type\n");
+        ret = FM_FAILURE;
+    }
+    return ret;
+}
+
+//set spacing for successive channels
+int FmRadioController :: SetChannelSpacing
+(
+    long spacing
+)
+{
+    int ret;
+
+    if (cur_fm_state == FM_ON) {
+        ret = FmIoctlsInterface::set_control(fd_driver,
+                               V4L2_CID_PRV_CHAN_SPACING, spacing);
+    } else {
+        ALOGE("FM is not in proper state to set the channel spacing\n");
+        ret = FM_FAILURE;
+    }
+    return ret;
+}
+
+int FmRadioController :: SetStereo
+(
+)
+{
+    int ret;
+
+    if((cur_fm_state != FM_OFF) &&
+       (cur_fm_state != FM_ON_IN_PROGRESS)) {
+       ret = FmIoctlsInterface::set_audio_mode(fd_driver,
+                                           STEREO);
+    }else {
+       ret = FM_FAILURE;
+    }
+    return ret;
+}
+
+int FmRadioController :: SetMono
+(
+)
+{
+    int ret;
+
+    if((cur_fm_state != FM_OFF) &&
+       (cur_fm_state != FM_ON_IN_PROGRESS)) {
+       ret = FmIoctlsInterface::set_audio_mode(fd_driver,
+                                              MONO);
+    }else {
+       ret = FM_FAILURE;
+    }
+    return ret;
+}
+
+bool FmRadioController :: GetSoftMute
+(
+)
+{
+    int ret = FM_SUCCESS;
+    long mode = SMUTE_DISABLED;
+
+    if((cur_fm_state != FM_OFF) &&
+       (cur_fm_state != FM_ON_IN_PROGRESS)) {
+       ret = FmIoctlsInterface::get_control(fd_driver,
+                            V4L2_CID_PRV_SOFT_MUTE, mode);
+       if(ret == FM_SUCCESS) {
+          ALOGI("FM Get soft mute is successful: %ld\n", mode);
+       }else {
+          ALOGE("FM Get soft mute failed");
+       }
+    }else {
+       ALOGE("FM is not in proper state for getting soft mute\n");
+       ret = FM_FAILURE;
+    }
+    return mode;
+}
+
+int FmRadioController :: Antenna_Switch(int antenna)
+{
+    int ret = 0;
+
+    if (antenna) {
+        ret = FmIoctlsInterface::set_control(fd_driver,
+                                     V4L2_CID_PRV_ANTENNA, 1);
+    } else {
+        ret = FmIoctlsInterface::set_control(fd_driver,
+                                     V4L2_CID_PRV_ANTENNA, 0);
+    }
+    ALOGD("%s, antenna type = %d [ret=%d]\n", __func__, antenna, ret);
+    return ret;
+}
+
+int FmRadioController :: get_fm_state
+(
+)
+{
+    return cur_fm_state;
+}
+
+void FmRadioController :: set_fm_state
+(
+    int state
+)
+{
+    pthread_mutex_lock(&mutex_fm_state);
+    cur_fm_state = state;
+    pthread_mutex_unlock(&mutex_fm_state);
+}
+
+void* FmRadioController :: handle_events
+(
+    void *arg
+)
+{
+    int bytesread;
+    char event_buff[STD_BUF_SIZE];
+    bool status = true;
+    FmRadioController *obj_p = static_cast<FmRadioController*>(arg);
+
+    while(status && !obj_p->event_listener_canceled) {
+        bytesread = FmIoctlsInterface::get_buffer(obj_p->fd_driver,
+                      event_buff, STD_BUF_SIZE, EVENT_IND);
+        for(int i = 0; i < bytesread; i++) {
+            status = obj_p->process_radio_events(event_buff[i]);
+            if(status == false) {
+                break;
+            }
+        }
+    }
+    return NULL;
+}
+
+int FmRadioController :: SetRdsGrpMask
+(
+    int mask
+)
+{
+    int ret;
+
+    if((cur_fm_state != FM_OFF) &&
+       (cur_fm_state != FM_OFF_IN_PROGRESS) &&
+       (cur_fm_state != FM_ON_IN_PROGRESS)) {
+        ret = FmIoctlsInterface::set_control(fd_driver,
+                      V4L2_CID_PRV_RDSGROUP_MASK, mask);
+    }else {
+        ret = FM_FAILURE;
+    }
+    return ret;
+}
+
+void FmRadioController :: handle_enabled_event
+(
+     void
+)
+{
+     ALOGI("FM handle ready Event\n");
+     FmIoctlsInterface::set_control(fd_driver,
+             V4L2_CID_PRV_AUDIO_PATH, AUDIO_DIGITAL_PATH);
+     FmIoctlsInterface::set_calibration(fd_driver);
+     pthread_mutex_lock(&mutex_turn_on_cond);
+     set_fm_state(FM_ON);
+     pthread_cond_broadcast(&turn_on_cond);
+     pthread_mutex_unlock(&mutex_turn_on_cond);
+}
+
+void FmRadioController :: handle_tuned_event
+(
+     void
+)
+{
+     long freq = -1;
+
+     ALOGI("FM handle Tune event\n");
+     freq = GetChannel();
+     switch(cur_fm_state) {
+         case FM_ON:
+            if(af_enabled && (freq != prev_freq)
+                && (prev_freq > 0)) {
+               ALOGI("AF jump happened\n");
+               is_af_jump_received = true;
+            }
+            break;
+         case FM_TUNE_IN_PROGRESS:
+            pthread_mutex_lock(&mutex_tune_compl_cond);
+            set_fm_state(FM_ON);
+            pthread_cond_broadcast(&tune_compl_cond);
+            pthread_mutex_unlock(&mutex_tune_compl_cond);
+            break;
+         case SEEK_IN_PROGRESS:
+            pthread_mutex_lock(&mutex_seek_compl_cond);
+            set_fm_state(FM_ON);
+            pthread_cond_broadcast(&seek_compl_cond);
+            pthread_mutex_unlock(&mutex_seek_compl_cond);
+            break;
+         case SCAN_IN_PROGRESS:
+            break;
+     }
+     prev_freq = freq;
+}
+
+void FmRadioController :: handle_seek_next_event
+(
+     void
+)
+{
+     ALOGI("FM handle seek next event\n");
+}
+
+void FmRadioController :: handle_seek_complete_event
+(
+     void
+)
+{
+     ALOGI("FM handle seek complete event\n");
+}
+
+void FmRadioController :: handle_raw_rds_event
+(
+     void
+)
+{
+
+}
+
+void FmRadioController :: handle_rt_event
+(
+     void
+)
+{
+     ALOGI("FM handle RT event\n");
+     is_rt_event_received = true;
+}
+
+void FmRadioController :: handle_ps_event
+(
+    void
+)
+{
+    ALOGI("FM handle PS event\n");
+    is_ps_event_received = true;
+}
+
+void FmRadioController :: handle_error_event
+(
+   void
+)
+{
+
+}
+
+void FmRadioController :: handle_below_th_event
+(
+   void
+)
+{
+
+}
+
+void FmRadioController :: handle_above_th_event
+(
+   void
+)
+{
+
+}
+
+void FmRadioController :: handle_stereo_event
+(
+   void
+)
+{
+
+}
+void FmRadioController :: handle_mono_event
+(
+  void
+)
+{
+
+}
+
+void FmRadioController :: handle_rds_aval_event
+(
+  void
+)
+{
+    ALOGI("Got rds_aval_event\n");
+    is_rds_support = true;
+}
+
+void FmRadioController :: handle_rds_not_aval_event
+(
+  void
+)
+{
+    ALOGI("Got rds_not_aval_event\n");
+}
+
+void FmRadioController :: handle_srch_list_event
+(
+  void
+)
+{
+    ALOGI("Got srch list event\n");
+    if (cur_fm_state == SCAN_IN_PROGRESS) {
+        pthread_mutex_lock(&mutex_scan_compl_cond);
+        set_fm_state(FM_ON);
+        pthread_cond_broadcast(&scan_compl_cond);
+        pthread_mutex_unlock(&mutex_scan_compl_cond);
+    }
+}
+
+void FmRadioController :: handle_af_list_event
+(
+  void
+)
+{
+    char raw_rds[STD_BUF_SIZE];
+    int ret;
+    int aflist_size;
+    ULINT lower_band;
+    int AfList[MAX_AF_LIST_SIZE];
+
+    ALOGI("Got af list event\n");
+    ret = FmIoctlsInterface::get_buffer(fd_driver,
+                     raw_rds, STD_BUF_SIZE, AF_LIST_IND);
+    lower_band = FmIoctlsInterface::get_lowerband_limit(fd_driver,
+                     lower_band);
+    ALOGI("raw_rds[0]: %d\n", (raw_rds[0] & 0xff));
+    ALOGI("raw_rds[1]: %d\n", (raw_rds[1] & 0xff));
+    ALOGI("raw_rds[2]: %d\n", (raw_rds[2] & 0xff));
+    ALOGI("raw_rds[3]: %d\n", (raw_rds[3] & 0xff));
+    ALOGI("raw_rds[4]: %d\n", (raw_rds[4] & 0xff));
+    ALOGI("raw_rds[5]: %d\n", (raw_rds[5] & 0xff));
+    ALOGI("raw_rds[6]: %d\n", (raw_rds[6] & 0xff));
+
+    aflist_size = raw_rds[AF_SIZE_IDX] & 0xff;
+    for(int i = 0; i < aflist_size; i++) {
+       AfList[i] = (raw_rds[AF_SIZE_IDX + i * NO_OF_BYTES_AF + 1] & 0xFF) |
+                   ((raw_rds[AF_SIZE_IDX + i * NO_OF_BYTES_AF + 2] & 0xFF) << 8) |
+                   ((raw_rds[AF_SIZE_IDX + i * NO_OF_BYTES_AF + 3] & 0xFF) << 16) |
+                   ((raw_rds[AF_SIZE_IDX + i * NO_OF_BYTES_AF + 4] & 0xFF) << 24);
+       ALOGI("AF: %d\n", AfList[i]);
+    }
+}
+
+void FmRadioController :: handle_disabled_event
+(
+  void
+)
+{
+     //Expected disabled
+     if(cur_fm_state == FM_OFF_IN_PROGRESS) {
+        ALOGI("Expected disabled event\n");
+     }else {//Enexpected disabled
+        ALOGI("Unexpected disabled event\n");
+     }
+
+     set_fm_state(FM_OFF);
+     close(fd_driver);
+     fd_driver = -1;
+
+     //allow tune function to exit
+     pthread_mutex_lock(&mutex_tune_compl_cond);
+     pthread_cond_broadcast(&tune_compl_cond);
+     pthread_mutex_unlock(&mutex_tune_compl_cond);
+     //allow scan function to exit
+     pthread_mutex_lock(&mutex_scan_compl_cond);
+     pthread_cond_broadcast(&scan_compl_cond);
+     pthread_mutex_unlock(&mutex_scan_compl_cond);
+     //Allow seek function to exit
+     pthread_mutex_lock(&mutex_seek_compl_cond);
+     pthread_cond_broadcast(&seek_compl_cond);
+     pthread_mutex_unlock(&mutex_seek_compl_cond);
+}
+
+void FmRadioController :: handle_rds_grp_mask_req_event
+(
+    void
+)
+{
+    SetRdsGrpMask(0);
+}
+
+void FmRadioController :: handle_rt_plus_event
+(
+    void
+)
+{
+    ALOGI("FM handle RT Plus event\n");
+}
+
+void FmRadioController :: handle_af_jmp_event
+(
+    void
+)
+{
+    long freq = -1;
+
+    freq = GetChannel();
+    ALOGI("FM handle AF Jumped event\n");
+    if(af_enabled && (freq != prev_freq)) {
+       ALOGI("AF Jump occured, prevfreq is: %ld, af freq is: %ld\n", prev_freq, freq);
+    }
+    prev_freq = freq;
+}
+
+void FmRadioController :: handle_ert_event
+(
+    void
+)
+{
+    ALOGI("FM handle ERT event\n");
+}
+
+bool FmRadioController :: process_radio_events
+(
+    int event
+)
+{
+    bool ret = true;
+
+    switch(event) {
+        case READY_EVENT:
+            handle_enabled_event();
+            break;
+        case TUNE_EVENT:
+            handle_tuned_event();
+            break;
+        case SEEK_COMPLETE_EVENT:
+            handle_seek_complete_event();
+            break;
+        case SCAN_NEXT_EVENT:
+            handle_seek_next_event();
+            break;
+        case RAW_RDS_EVENT:
+            handle_raw_rds_event();
+            break;
+        case RT_EVENT:
+            handle_rt_event();
+            break;
+        case PS_EVENT:
+            handle_ps_event();
+            break;
+        case ERROR_EVENT:
+            handle_error_event();
+            break;
+        case BELOW_TH_EVENT:
+            handle_below_th_event();
+            break;
+        case ABOVE_TH_EVENT:
+            handle_above_th_event();
+            break;
+        case STEREO_EVENT:
+            handle_stereo_event();
+            break;
+        case MONO_EVENT:
+            handle_mono_event();
+            break;
+        case RDS_AVAL_EVENT:
+            handle_rds_aval_event();
+            break;
+        case RDS_NOT_AVAL_EVENT:
+            handle_rds_not_aval_event();
+            break;
+        case SRCH_LIST_EVENT:
+            handle_srch_list_event();
+            break;
+        case AF_LIST_EVENT:
+            handle_af_list_event();
+            break;
+        case DISABLED_EVENT:
+            handle_disabled_event();
+            ret = false;
+            break;
+        case RDS_GRP_MASK_REQ_EVENT:
+            handle_rds_grp_mask_req_event();
+            break;
+        case RT_PLUS_EVENT:
+            handle_rt_plus_event();
+            break;
+        case ERT_EVENT:
+            handle_ert_event();
+            break;
+        case AF_JMP_EVENT:
+            handle_af_jmp_event();
+            break;
+        default:
+            break;
+    }
+    return ret;
+}
diff --git a/libfm_jni/FmRadioController.h b/libfm_jni/FmRadioController.h
new file mode 100644
index 0000000..6ce906e
--- /dev/null
+++ b/libfm_jni/FmRadioController.h
@@ -0,0 +1,125 @@
+/*
+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 __FM_RADIO_CTRL_H__
+#define __FM_RADIO_CTRL_H___
+
+#include <pthread.h>
+#include <ctime>
+
+class FmRadioController
+{
+    private:
+        int cur_fm_state;
+        char af_enabled;
+        bool seek_scan_canceled;
+        bool is_rds_support;
+        bool is_ps_event_received = false;
+        bool is_rt_event_received = false;
+        bool is_af_jump_received = false;
+        bool event_listener_canceled;
+        pthread_mutex_t mutex_fm_state;
+        pthread_mutex_t mutex_turn_on_cond;
+        pthread_mutex_t mutex_seek_compl_cond;
+        pthread_mutex_t mutex_scan_compl_cond;
+        pthread_mutex_t mutex_tune_compl_cond;
+        pthread_cond_t turn_on_cond;
+        pthread_cond_t seek_compl_cond;
+        pthread_cond_t scan_compl_cond;
+        pthread_cond_t tune_compl_cond;
+        char rds_enabled;
+        long int prev_freq;
+        int fd_driver;
+        pthread_t event_listener_thread;
+        int SetRdsGrpMask(int mask);
+        int SetRdsGrpProcessing(int grps);
+        void handle_enabled_event(void);
+        void handle_tuned_event(void);
+        void handle_seek_next_event(void);
+        void handle_seek_complete_event(void);
+        void handle_raw_rds_event(void);
+        void handle_rt_event(void);
+        void handle_ps_event(void);
+        void handle_error_event(void);
+        void handle_below_th_event(void);
+        void handle_above_th_event(void);
+        void handle_stereo_event(void);
+        void handle_mono_event(void);
+        void handle_rds_aval_event(void);
+        void handle_rds_not_aval_event(void);
+        void handle_srch_list_event(void);
+        void handle_af_list_event(void);
+        void handle_disabled_event(void);
+        void handle_rds_grp_mask_req_event(void);
+        void handle_rt_plus_event(void);
+        void handle_ert_event(void);
+        void handle_af_jmp_event(void);
+        void set_fm_state(int state);
+        struct timespec set_time_out(int secs);
+        int GetStationList(uint16_t *scan_tbl, int *max_cnt);
+        int EnableRDS(void);
+        int DisableRDS(void);
+        int EnableAF(void);
+        int DisableAF(void);
+        int SetStereo(void);
+        int SetMono(void);
+        int MuteOn(void);
+        int MuteOff(void);
+        int get_fm_state(void);
+        long GetCurrentRSSI(void);
+        bool GetSoftMute(void);
+    public:
+       FmRadioController();
+       ~FmRadioController();
+       int open_dev(void);
+       int close_dev();
+       int Pwr_Up(int freq);
+       int Pwr_Down(void);
+       long GetChannel(void);
+       int TuneChannel(long);
+       bool IsRds_support();
+       int ScanList(uint16_t *scan_tbl, int *max_cnt);
+       int Seek(int dir);
+       int ReadRDS(void);
+       int Get_ps(char *ps, int *ps_len);
+       int Get_rt(char *rt, int *rt_len);
+       int Get_AF_freq(uint16_t *ret_freq);
+       int SetDeConstant(long );
+       int SetSoftMute(bool mode);
+       int Set_mute(bool mute);
+       int SetBand(long);
+       int SetChannelSpacing(long);
+       int Stop_Scan_Seek(void);
+       int Turn_On_Off_Rds(bool onoff);
+       int Antenna_Switch(int antenna);
+       static void* handle_events(void *arg);
+       bool process_radio_events(int event);
+};
+
+#endif
diff --git a/libfm_jni/LibfmJni.cpp b/libfm_jni/LibfmJni.cpp
new file mode 100644
index 0000000..d1a0942
--- /dev/null
+++ b/libfm_jni/LibfmJni.cpp
@@ -0,0 +1,397 @@
+/*
+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 <jni.h>
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+#include <utils/Log.h>
+#include "FmRadioController.h"
+#include "FM_Const.h"
+
+static FmRadioController * pFMRadio;
+
+jboolean OpenFd(JNIEnv *env, jobject thiz)
+{
+    int ret = 0;
+    pFMRadio = new FmRadioController();
+    if (pFMRadio)
+        ret = pFMRadio->open_dev();
+    else
+        ret = JNI_FALSE;
+    ALOGD("%s, [ret=%d]\n", __func__, ret);
+    return ret? JNI_FALSE: JNI_TRUE;
+}
+
+jboolean CloseFd(JNIEnv *env, jobject thiz)
+{
+    int ret = 0;
+
+    if (pFMRadio)
+        ret = pFMRadio->close_dev();
+    else
+        ret = JNI_FALSE;
+
+    ALOGD("%s, [ret=%d]\n", __func__, ret);
+    return ret? JNI_FALSE: JNI_TRUE;
+}
+
+jboolean TurnOn(JNIEnv *env, jobject thiz, jfloat freq)
+{
+    int ret = 0;
+    int tmp_freq;
+
+    ALOGI("%s, [freq=%d]\n", __func__, (int)freq);
+    tmp_freq = (int)(freq * FREQ_MULT);   //Eg, 87.5 * 1000 --> 87500
+    if (!pFMRadio) {
+        pFMRadio = new FmRadioController();
+    }
+    if (pFMRadio)
+        ret = pFMRadio->Pwr_Up(tmp_freq);
+    else
+        ret = JNI_FALSE;
+
+    ALOGD("%s, [ret=%d]\n", __func__, ret);
+    return ret?JNI_FALSE:JNI_TRUE;
+}
+
+jboolean TurnOff(JNIEnv *env, jobject thiz, jint type)
+{
+    int ret = 0;
+
+    if (pFMRadio)
+        ret = pFMRadio->Pwr_Down();
+    else
+        ret = JNI_FALSE;
+
+    ALOGD("%s, [ret=%d]\n", __func__, ret);
+    if (pFMRadio) {
+        delete pFMRadio;
+        pFMRadio = NULL;
+    }
+    return ret?JNI_FALSE:JNI_TRUE;
+}
+
+jboolean SetFreq(JNIEnv *env, jobject thiz, jfloat freq)
+{
+    int ret = 0;
+    int tmp_freq;
+
+    tmp_freq = (int)(freq * FREQ_MULT);        //Eg, 87.5 * 10 --> 875
+    if (pFMRadio)
+        ret = pFMRadio->TuneChannel(tmp_freq);
+    else
+        ret = JNI_FALSE;
+
+    ALOGD("%s, [ret=%d]\n", __func__, ret);
+    return ret?JNI_FALSE:JNI_TRUE;
+}
+
+jfloat Seek(JNIEnv *env, jobject thiz, jfloat freq, jboolean isUp)
+{
+    int ret = 0;
+    int tmp_freq;
+    int ret_freq;
+    float val;
+
+    tmp_freq = (int)(freq * FREQ_MULT); //Eg, 87.55 * 100 --> 87550
+    if (pFMRadio)
+        ret = pFMRadio->Set_mute(true);
+    else
+        ret = JNI_FALSE;
+    if (ret) {
+        ALOGE("%s, error, [ret=%d]\n", __func__, ret);
+    }
+    ALOGD("%s, [mute] [ret=%d]\n", __func__, ret);
+    if (pFMRadio)
+        ret = pFMRadio->Seek((int)isUp);
+    else
+        ret = JNI_FALSE;
+    if (ret < 0) {
+        ret_freq = tmp_freq; //seek error, so use original freq
+    }
+
+    ALOGD("%s, [freq=%d] [ret=%d]\n", __func__, ret_freq, ret);
+
+    val = (float)ret/FREQ_MULT;   //Eg, 8755 / 100 --> 87.55
+
+    return val;
+}
+
+jshortArray ScanList(JNIEnv *env, jobject thiz)
+{
+    int ret = 0;
+    jshortArray scanList;
+    int chl_cnt = FM_SCAN_CH_SIZE_MAX;
+    uint16_t ScanTBL[FM_SCAN_CH_SIZE_MAX];
+
+    if (pFMRadio)
+        ret = pFMRadio->ScanList(ScanTBL, &chl_cnt);
+    else
+        ret = JNI_FALSE;
+    if (ret < 0) {
+        ALOGE("scan failed!\n");
+        scanList = NULL;
+        goto out;
+    }
+    if (chl_cnt > 0) {
+        scanList = env->NewShortArray(chl_cnt);
+        env->SetShortArrayRegion(scanList, 0, chl_cnt, (const jshort*)&ScanTBL[0]);
+    } else {
+        ALOGE("cnt error, [cnt=%d]\n", chl_cnt);
+        scanList = NULL;
+    }
+
+out:
+    ALOGD("%s, [cnt=%d] [ret=%d]\n", __func__, chl_cnt, ret);
+    return scanList;
+}
+
+jshort GetRdsEvent(JNIEnv *env, jobject thiz)
+{
+    int ret = JNI_FALSE;
+
+    if (pFMRadio)
+        ret = pFMRadio->ReadRDS();
+
+    return ret;
+}
+
+jbyteArray GetPsText(JNIEnv *env, jobject thiz)
+{
+    int ret = 0;
+    jbyteArray PS;
+    char ps[MAX_PS_LEN];
+    int ps_len = 0;
+
+    if (pFMRadio)
+        ret = pFMRadio->Get_ps(ps, &ps_len);
+    else
+        ret = JNI_FALSE;
+    if (ret) {
+        ALOGE("%s, error, [ret=%d]\n", __func__, ret);
+        return NULL;
+    }
+    PS = env->NewByteArray(ps_len);
+    env->SetByteArrayRegion(PS, 0, ps_len, (const jbyte*)ps);
+    ALOGD("%s, [ret=%d]\n", __func__, ret);
+    return PS;
+}
+
+jbyteArray GetRtText(JNIEnv *env, jobject thiz)
+{
+    int ret = 0;
+    jbyteArray RadioText;
+    char rt[MAX_RT_LEN];
+    int rt_len = 0;
+
+    if (pFMRadio)
+        ret = pFMRadio->Get_rt(rt, &rt_len);
+    else
+        ret = JNI_FALSE;
+    if (ret) {
+        ALOGE("%s, error, [ret=%d]\n", __func__, ret);
+        return NULL;
+    }
+    RadioText = env->NewByteArray(rt_len);
+    env->SetByteArrayRegion(RadioText, 0, rt_len, (const jbyte*)rt);
+    ALOGD("%s, [ret=%d]\n", __func__, ret);
+    return RadioText;
+}
+
+jshort GetAfFreq(JNIEnv *env, jobject thiz)
+{
+    int ret = 0;
+    jshort ret_freq = 0;
+
+    if (pFMRadio)
+        ret = pFMRadio->Get_AF_freq((uint16_t*)&ret_freq);
+    else
+        ret = JNI_FALSE;
+    if (ret) {
+        ALOGE("%s, error, [ret=%d]\n", __func__, ret);
+        return 0;
+    }
+    ALOGD("%s, [ret_freq=%d]\n", __func__, ret_freq);
+    return ret_freq;
+}
+
+jint SetRds(JNIEnv *env, jobject thiz, jboolean rdson)
+{
+    int ret = 0;
+
+    if (pFMRadio)
+        ret = pFMRadio->Turn_On_Off_Rds(rdson);
+    else
+        ret = JNI_FALSE;
+    if (ret) {
+        ALOGE("%s, error, [ret=%d]\n", __func__, ret);
+    }
+    ALOGD("%s, [rdson=%d] [ret=%d]\n", __func__, rdson, ret);
+    return ret?JNI_FALSE:JNI_TRUE;
+}
+
+jboolean StopSrch(JNIEnv *env, jobject thiz)
+{
+    int ret = 0;
+
+    if (pFMRadio)
+        ret = pFMRadio->Stop_Scan_Seek();
+    else
+        ret = JNI_FALSE;
+    if (ret) {
+        ALOGE("%s, error, [ret=%d]\n", __func__, ret);
+    }
+    ALOGD("%s, [ret=%d]\n", __func__, ret);
+    return ret?JNI_FALSE:JNI_TRUE;
+}
+
+jint SetMute(JNIEnv *env, jobject thiz, jboolean mute)
+{
+    int ret = 0;
+
+    if (pFMRadio)
+        ret = pFMRadio->Set_mute(mute);
+    else
+        ret = JNI_FALSE;
+    if (ret) {
+        ALOGE("%s, error, [ret=%d]\n", __func__, ret);
+    }
+    ALOGD("%s, [mute=%d] [ret=%d]\n", __func__, (int)mute, ret);
+    return ret?JNI_FALSE:JNI_TRUE;
+}
+
+/******************************************
+ * Inquiry if RDS is support in driver.
+ * Parameter:
+ *      None
+ *Return Value:
+ *      1: support
+ *      0: NOT support
+ *      -1: error
+ ******************************************/
+jint IsRdsSupport(JNIEnv *env, jobject thiz)
+{
+    int ret = 0;
+
+    if (pFMRadio)
+        ret = pFMRadio->IsRds_support();
+    else
+        ret = JNI_FALSE;
+    if (!ret) {
+        ALOGE("%s, error, [ret=%d]\n", __func__, ret);
+    }
+    ALOGD("%s, [ret=%d]\n", __func__, ret);
+    return ret;
+}
+
+/******************************************
+ * SwitchAntenna
+ * Parameter:
+ *      antenna:
+                0 : switch to long antenna
+                1: switch to short antenna
+ *Return Value:
+ *          0: Success
+ *          1: Failed
+ *          2: Not support
+ ******************************************/
+jint SetAntenna(JNIEnv *env, jobject thiz, jint antenna)
+{
+    int ret = 0;
+    jint jret = 0;
+    int ana = -1;
+
+    if (0 == antenna) {
+        ana = FM_LONG_ANA;
+    } else if (1 == antenna) {
+        ana = FM_SHORT_ANA;
+    } else {
+        ALOGE("%s:fail, para error\n", __func__);
+        jret = JNI_FALSE;
+        goto out;
+    }
+    if (pFMRadio)
+        ret = pFMRadio->Antenna_Switch(ana);
+    else
+        ret = JNI_FALSE;
+    if (ret) {
+        ALOGE("switchAntenna(), error\n");
+        jret = 1;
+    } else {
+        jret = 0;
+    }
+out:
+    ALOGD("%s: [antenna=%d] [ret=%d]\n", __func__, ana, ret);
+    return jret;
+}
+
+static const char *classPathNameFM = "com/android/fmradio/FmNative";
+
+static JNINativeMethod gMethods[] = {
+    {"openDev",       "()Z",   (void*)OpenFd },
+    {"closeDev",      "()Z",   (void*)CloseFd },
+    {"powerUp",       "(F)Z",  (void*)TurnOn },
+    {"powerDown",     "(I)Z",  (void*)TurnOff },
+    {"tune",          "(F)Z",  (void*)SetFreq },
+    {"seek",          "(FZ)F", (void*)Seek },
+    {"autoScan",      "()[S",  (void*)ScanList },
+    {"stopScan",      "()Z",   (void*)StopSrch },
+    {"setRds",        "(Z)I",  (void*)SetRds  },
+    {"readRds",       "()S",   (void*)GetRdsEvent },
+    {"getPs",         "()[B",  (void*)GetPsText },
+    {"getLrText",     "()[B",  (void*)GetRtText},
+    {"activeAf",      "()S",   (void*)GetAfFreq},
+    {"setMute",       "(Z)I",  (void*)SetMute},
+    {"isRdsSupport",  "()I",   (void*)IsRdsSupport},
+    {"switchAntenna", "(I)I",  (void*)SetAntenna},
+};
+
+int register_android_hardware_fm(JNIEnv* env)
+{
+        return jniRegisterNativeMethods(env, classPathNameFM, gMethods, NELEM(gMethods));
+}
+
+jint JNI_OnLoad(JavaVM *jvm, void *reserved)
+{
+  JNIEnv *e;
+  int status;
+   ALOGE("FM : loading FM-JNI\n");
+
+   if(jvm->GetEnv((void **)&e, JNI_VERSION_1_6)) {
+       ALOGE("JNI version mismatch error");
+      return JNI_ERR;
+   }
+
+   if ((status = register_android_hardware_fm(e)) < 0) {
+       ALOGE("jni adapter service registration failure, status: %d", status);
+      return JNI_ERR;
+   }
+   return JNI_VERSION_1_6;
+}
+