diff options
author | Venkateshwarlu Domakonda <vdomak@codeaurora.org> | 2015-03-24 10:28:37 +0530 |
---|---|---|
committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2015-04-09 03:27:16 -0700 |
commit | acdd9a4aa651b66b3a69cb1ec4f2b3fd1fb4aaed (patch) | |
tree | 77c0c1248da3558ab5b26cde16761d6dd3805203 /libfm_jni | |
parent | ee21b948384ed2ae1f133f53f4d52b8ff245ee62 (diff) | |
download | android_hardware_qcom_fm-acdd9a4aa651b66b3a69cb1ec4f2b3fd1fb4aaed.tar.gz android_hardware_qcom_fm-acdd9a4aa651b66b3a69cb1ec4f2b3fd1fb4aaed.tar.bz2 android_hardware_qcom_fm-acdd9a4aa651b66b3a69cb1ec4f2b3fd1fb4aaed.zip |
libfmjni: Implement hal layer
Implement HAL layer that can be plugged in
with Android FM APP to get fm functionalities.
Change-Id: Id16d37a320fdbacb505ba7ecc431622db17bd4d7
Diffstat (limited to 'libfm_jni')
-rw-r--r-- | libfm_jni/Android.mk | 29 | ||||
-rw-r--r-- | libfm_jni/ConfFileParser.cpp | 918 | ||||
-rw-r--r-- | libfm_jni/ConfFileParser.h | 82 | ||||
-rw-r--r-- | libfm_jni/ConfigFmThs.cpp | 605 | ||||
-rw-r--r-- | libfm_jni/ConfigFmThs.h | 183 | ||||
-rw-r--r-- | libfm_jni/FM_Const.h | 368 | ||||
-rw-r--r-- | libfm_jni/FmIoctlsInterface.cpp | 384 | ||||
-rw-r--r-- | libfm_jni/FmIoctlsInterface.h | 57 | ||||
-rw-r--r-- | libfm_jni/FmPerformanceParams.cpp | 545 | ||||
-rw-r--r-- | libfm_jni/FmPerformanceParams.h | 78 | ||||
-rw-r--r-- | libfm_jni/FmRadioController.cpp | 1434 | ||||
-rw-r--r-- | libfm_jni/FmRadioController.h | 125 | ||||
-rw-r--r-- | libfm_jni/LibfmJni.cpp | 397 |
13 files changed, 5205 insertions, 0 deletions
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; +} + |