Revert "FM: Remove Ioctl base code"
This reverts commit 5156eb1ae2b6a385ccfee8a1ddadf5811dce8f82.
Change-Id: I68fb8a81cadde7a49d83f9189d592e88d1b35ba8
diff --git a/fm_hci/fm_hci.c b/fm_hci/fm_hci.c
new file mode 100644
index 0000000..ff932b9
--- /dev/null
+++ b/fm_hci/fm_hci.c
@@ -0,0 +1,808 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "fm_hci_helium"
+
+#include <assert.h>
+#include <utils/Log.h>
+
+#include "bt_hci_bdroid.h"
+#include "bt_vendor_lib.h"
+#include "userial.h"
+#include "fm_hci.h"
+#include "wcnss_hci.h"
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <cutils/sockets.h>
+#include <pthread.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <cutils/properties.h>
+#include <signal.h>
+
+static int fm_hal_fd =0;
+
+#define FM_VND_SERVICE_START "wc_transport.start_fmhci"
+#define WAIT_TIMEOUT 200000 /* 200*1000us */
+
+static void fm_hci_exit(void *arg);
+static int power(struct fm_hci_t *hci, fm_power_state_t state);
+
+static void event_notification(struct fm_hci_t *hci, uint16_t event)
+{
+ pthread_mutex_lock(&hci->event_lock);
+ ALOGI("%s: Notifying worker thread with event: %d", __func__, event);
+ ready_events |= event;
+ pthread_cond_broadcast(&hci->event_cond);
+ pthread_mutex_unlock(&hci->event_lock);
+}
+
+static void rx_thread_exit_handler(int sig)
+{
+ ALOGD("%s: sig = 0x%x", __func__, sig);
+ if (sig == SIGUSR1) {
+ ALOGE("Got the signal.. exiting");
+ pthread_exit(NULL);
+ }
+}
+
+static int vendor_init(struct fm_hci_t *hci)
+{
+ void *dlhandle = hci->dlhandle = NULL;
+ unsigned char bdaddr[] = {0xaa, 0xbb, 0xcc, 0x11, 0x22, 0x33};
+
+ dlhandle = dlopen("libbt-vendor.so", RTLD_NOW);
+ if (!dlhandle) {
+ ALOGE("!!! Failed to load libbt-vendor.so !!!");
+ goto err;
+ }
+
+ hci->vendor = (bt_vendor_interface_t *) dlsym(dlhandle, "BLUETOOTH_VENDOR_LIB_INTERFACE");
+ if (!hci->vendor) {
+ ALOGE("!!! Failed to get bt vendor interface !!!");
+ goto err;
+ }
+
+ ALOGI("FM-HCI: Registering the WCNSS HAL library by passing CBs and BD addr.");
+ if (hci->vendor->init(&fm_vendor_callbacks, bdaddr) !=
+ FM_HC_STATUS_SUCCESS) {
+ ALOGE("FM vendor interface init failed");
+ goto err;
+ }
+
+ return FM_HC_STATUS_SUCCESS;
+
+err:
+ return FM_HC_STATUS_FAIL;
+}
+
+static void vendor_close(struct fm_hci_t *hci)
+{
+ void *dlhandle = hci->dlhandle;
+
+ if (hci->vendor)
+ hci->vendor->cleanup();
+ if (dlhandle) {
+ dlclose(dlhandle);
+ dlhandle = NULL;
+ }
+ hci->vendor = NULL;
+}
+
+/* De-queues the FM CMD from the struct transmit_queue_t */
+static void dequeue_fm_tx_cmd(struct fm_hci_t *hci)
+{
+ struct transmit_queue_t *temp;
+ uint16_t count = 0, len = 0;
+
+ ALOGD("%s", __func__);
+ while (1) {
+ pthread_mutex_lock(&hci->tx_q_lock);
+ temp = hci->first;
+ if (!temp) {
+ ALOGI("No FM CMD available in the Queue\n");
+ pthread_mutex_unlock(&hci->tx_q_lock);
+ return;
+ } else {
+ hci->first = temp->next;
+ }
+ pthread_mutex_unlock(&hci->tx_q_lock);
+
+ pthread_mutex_lock(&hci->credit_lock);
+wait_for_cmd_credits:
+ while (hci->command_credits == 0) {
+ pthread_cond_wait(&hci->cmd_credits_cond, &hci->credit_lock);
+ }
+
+ /* Check if we really got the command credits */
+ if (hci->command_credits) {
+
+ len = sizeof(struct fm_command_header_t) + temp->hdr->len;
+again:
+ /* Use socket 'fd' to send the command down to WCNSS Filter */
+ count = write(hci->fd, (uint8_t *)temp->hdr + count, len);
+
+ if (count < len) {
+ len -= count;
+ goto again;
+ }
+ count = 0;
+
+ /* Decrement cmd credits by '1' after sending the cmd*/
+ hci->command_credits--;
+ if (temp->hdr)
+ free(temp->hdr);
+ free(temp);
+ } else {
+ if (!lib_running) {
+ pthread_mutex_unlock(&hci->credit_lock);
+ break;
+ }
+ goto wait_for_cmd_credits;
+ }
+ pthread_mutex_unlock(&hci->credit_lock);
+ }
+}
+
+static int read_fm_event(struct fm_hci_t *hci, struct fm_event_header_t *pbuf, int len)
+{
+ fd_set readFds;
+ sigset_t sigmask, emptymask;
+ int n = 0, ret = -1, evt_len = -1,status=0;
+ volatile int fd = hci->fd;
+ struct sigaction action;
+
+ sigemptyset(&sigmask);
+ sigaddset(&sigmask, SIGUSR1);
+ if (sigprocmask(SIG_BLOCK, &sigmask, NULL) == -1) {
+ ALOGE("failed to sigprocmask");
+ }
+ memset(&action, 0, sizeof(struct sigaction));
+ sigemptyset(&action.sa_mask);
+ action.sa_flags = 0;
+ action.sa_handler = rx_thread_exit_handler;
+
+ sigemptyset(&emptymask);
+
+ if (sigaction(SIGUSR1, &action, NULL) < 0) {
+ ALOGE("%s:sigaction failed", __func__);
+ }
+
+ while (lib_running)
+ {
+ FD_ZERO(&readFds);
+ FD_SET(fd, &readFds);
+
+ ALOGV("%s: Waiting for events from WCNSS FILTER...\n", __func__);
+
+ /* Wait for event/data from WCNSS Filter */
+ n = pselect(fd+1, &readFds, NULL, NULL, NULL, &emptymask);
+ if (n > 0)
+ {
+ /* Check if event is available or not */
+ if (FD_ISSET(fd, &readFds)) {
+ ret = read(fd, (uint8_t *)pbuf, (size_t)(sizeof(struct fm_event_header_t) + MAX_FM_EVT_PARAMS));
+ if (0 == ret) {
+ ALOGV("%s: read() returned '0' bytes\n", __func__);
+ break;
+ }
+ else {
+ ALOGV("%s: read() returned %d bytes of FM event/data\n", __func__, ret);
+ while (ret > 0) {
+ pthread_mutex_lock(&hci->credit_lock);
+ if (pbuf->evt_code == FM_CMD_COMPLETE) {
+ hci->command_credits = pbuf->params[0];
+ pthread_cond_signal(&hci->cmd_credits_cond);
+ } else if (pbuf->evt_code == FM_CMD_STATUS) {
+ hci->command_credits = pbuf->params[1];
+ pthread_cond_signal(&hci->cmd_credits_cond);
+ } else if (pbuf->evt_code == FM_HW_ERR_EVENT) {
+ ALOGI("%s: FM H/w Err Event Recvd. Event Code: 0x%2x", __func__, pbuf->evt_code);
+ /* remove until support added */
+ //hci->vendor->ssr_cleanup(0x22);
+ status = power(hci, FM_RADIO_DISABLE);
+ if (status < 0) {
+ ALOGE("power off fm radio failed during SSR ");
+ }
+ } else {
+ ALOGE("%s: Not CS/CC Event: Recvd. Event Code: 0x%2x", __func__, pbuf->evt_code);
+ }
+ pthread_mutex_unlock(&hci->credit_lock);
+
+ evt_len = pbuf->evt_len;
+
+ /* Notify 'hci_tx_thread' about availability of event or data */
+ ALOGI("%s: \nNotifying 'hci_tx_thread' availability of FM event or data...\n", __func__);
+ event_notification(hci, HC_EVENT_RX);
+
+ if (hci->cb && hci->cb->process_event)
+ hci->cb->process_event((uint8_t *)pbuf);
+ else
+ ALOGE("%s: ASSERT $$$$$$ Callback function NULL $$$$$", __func__);
+
+ ret = ret - (evt_len + 3);
+ ALOGD("%s: Length of available bytes @ HCI Layer: %d", __func__, ret);
+ if (ret > 0) {
+ ALOGE("%s: Remaining bytes of event/data: %d", __func__, ret);
+ pbuf = (struct fm_event_header_t *)&pbuf->params[evt_len];
+ }
+ }
+ } //end of processing the event
+
+ } else
+ ALOGV("%s: No data available, though select returned!!!\n", __func__);
+ }
+ else if (n < 0) {
+ ALOGE("%s: select() failed with return value: %d", __func__, ret);
+ lib_running =0;
+ }
+ else if (n == 0)
+ ALOGE("%s: select() timeout!!!", __func__);
+ }
+
+ return ret;
+}
+
+static void *hci_read_thread(void *arg)
+{
+ int length = 0;
+ struct fm_hci_t *hci = (struct fm_hci_t *)arg;
+
+ struct fm_event_header_t *evt_buf = (struct fm_event_header_t *) malloc(sizeof(struct fm_event_header_t) + MAX_FM_EVT_PARAMS);
+
+ if (!evt_buf) {
+ ALOGE("%s: Memory allocation failed for evt_buf", __func__);
+ goto cleanup;
+ }
+
+ length = read_fm_event(hci, evt_buf, sizeof(struct fm_event_header_t) + MAX_FM_EVT_PARAMS);
+ ALOGD("length=%d\n",length);
+ if(length <=0) {
+ lib_running =0;
+ }
+ goto exit;
+
+cleanup:
+ lib_running = 0;
+ hci = NULL;
+
+exit:
+ ALOGV("%s: Leaving hci_read_thread()", __func__);
+ if (evt_buf)
+ free(evt_buf);
+ pthread_exit(NULL);
+ return arg;
+}
+
+int connect_to_local_fmsocket(char* name) {
+ socklen_t len; int sk = -1;
+
+ ALOGE("%s: ACCEPT ", __func__);
+ sk = socket(AF_LOCAL, SOCK_STREAM, 0);
+ if (sk < 0) {
+ ALOGE("Socket creation failure");
+ return -1;
+ }
+
+ if(socket_local_client_connect(sk, name,
+ ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM) < 0)
+ {
+ ALOGE("failed to connect (%s)", strerror(errno));
+ close(sk);
+ sk = -1;
+ } else {
+ ALOGE("%s: Connection succeeded\n", __func__);
+ }
+ return sk;
+}
+
+/*
+ * Reads the FM-CMDs from the struct transmit_queue_t and sends it down to WCNSS Filter
+ * Reads events sent by the WCNSS Filter and copies onto RX_Q
+ */
+static void* hci_tx_thread(void *arg)
+{
+ uint16_t events;
+ struct fm_hci_t *hci = (struct fm_hci_t *)arg;
+
+ while (lib_running) {
+ pthread_mutex_lock(&hci->event_lock);
+ if (!(ready_events & HC_EVENT_TX))
+ pthread_cond_wait(&hci->event_cond, &hci->event_lock);
+ ALOGE("%s: ready_events= %d", __func__, ready_events);
+ events = ready_events;
+ if (ready_events & HC_EVENT_TX)
+ ready_events &= (~HC_EVENT_TX);
+ if (ready_events & HC_EVENT_RX)
+ ready_events &= (~HC_EVENT_RX);
+ pthread_mutex_unlock(&hci->event_lock);
+
+ if (events & HC_EVENT_TX) {
+ dequeue_fm_tx_cmd(hci);
+ }
+ if (events & HC_EVENT_RX) {
+ ALOGI("\n##### FM-HCI Task : EVENT_RX available #####\n");
+ }
+ if (events & HC_EVENT_EXIT) {
+ ALOGE("GOT HC_EVENT_EXIT.. exiting");
+ break;
+ }
+ }
+
+ ALOGV("%s: ##### Exiting hci_tx_thread Worker thread!!! #####", __func__);
+ return NULL;
+}
+
+void stop_fmhal_service() {
+ char value[PROPERTY_VALUE_MAX] = {'\0'};
+ ALOGI("%s: Entry ", __func__);
+ property_get(FM_VND_SERVICE_START, value, "false");
+ if (strcmp(value, "false") == 0) {
+ ALOGI("%s: fmhal service has been stopped already", __func__);
+// return;
+ }
+ close(fm_hal_fd);
+ fm_hal_fd = -1;
+ property_set(FM_VND_SERVICE_START, "false");
+ property_set("wc_transport.fm_service_status", "0");
+ ALOGI("%s: Exit ", __func__);
+}
+
+void start_fmhal_service() {
+ ALOGI("%s: Entry ", __func__);
+ int i, init_success = 0;
+ char value[PROPERTY_VALUE_MAX] = {'\0'};
+
+ property_get(FM_VND_SERVICE_START, value, false);
+
+ if (strcmp(value, "true") == 0) {
+ ALOGI("%s: hci_filter has been started already", __func__);
+ return;
+ }
+ // property_set("wc_transport.fm_service_status", "0");
+ usleep(100 * 1000); // 100 msecs
+ property_set(FM_VND_SERVICE_START, "true");
+ ALOGI("%s: %s set to true ", __func__, FM_VND_SERVICE_START );
+ for(i=0; i<45; i++) {
+ property_get("wc_transport.fm_service_status", value, "0");
+ if (strcmp(value, "1") == 0) {
+ ALOGI("%s: wc_transport.fm_service_status set to %s", __func__,value);
+ init_success = 1;
+ break;
+ } else {
+ usleep(WAIT_TIMEOUT);
+ }
+ }
+ ALOGI("start_fmhal_service status:%d after %f seconds \n", init_success, 0.2*i);
+
+ ALOGI("%s: Exit ", __func__);
+}
+
+static int start_tx_thread(struct fm_hci_t *hci)
+{
+ struct sched_param param;
+ int policy, result;
+
+ ALOGI("FM-HCI: Creating the FM-HCI TASK...");
+ if (pthread_create(&hci->tx_thread, NULL, hci_tx_thread, hci) != 0)
+ {
+ ALOGE("pthread_create failed!");
+ lib_running = 0;
+ return FM_HC_STATUS_FAIL;
+ }
+
+ if(pthread_getschedparam(hci->tx_thread, &policy, ¶m)==0)
+ {
+ policy = SCHED_NORMAL;
+#if (BTHC_LINUX_BASE_POLICY!=SCHED_NORMAL)
+ param.sched_priority = BTHC_MAIN_THREAD_PRIORITY;
+#endif
+ result = pthread_setschedparam(hci->tx_thread, policy, ¶m);
+ if (result != 0)
+ {
+ ALOGW("libbt-hci init: pthread_setschedparam failed (%d)", \
+ result);
+ }
+ } else
+ ALOGI("FM-HCI: Failed to get the Scheduling parameters!!!");
+
+ return FM_HC_STATUS_SUCCESS;
+}
+
+static void stop_tx_thread(struct fm_hci_t *hci)
+{
+ int ret;
+
+ ALOGV("%s++", __func__);
+ if ((ret = pthread_kill(hci->tx_thread, SIGUSR1))
+ == FM_HC_STATUS_SUCCESS) {
+ ALOGE("%s:pthread_join", __func__);
+ if ((ret = pthread_join(hci->tx_thread, NULL)) != FM_HC_STATUS_SUCCESS)
+ ALOGE("Error joining tx thread, error = %d (%s)",
+ ret, strerror(ret));
+ } else {
+ ALOGE("Error killing tx thread, error = %d (%s)",
+ ret, strerror(ret));
+ }
+}
+
+static void *hci_mon_thread(void *arg)
+{
+ struct fm_hci_t *hci = (struct fm_hci_t *)arg;
+ uint16_t events;
+ ALOGV("%s", __func__);
+
+ while (lib_running) {
+ pthread_mutex_lock(&hci->event_lock);
+ if (!(ready_events & HC_EVENT_EXIT))
+ pthread_cond_wait(&hci->event_cond, &hci->event_lock);
+ events = ready_events;
+ if (ready_events & HC_EVENT_EXIT)
+ ready_events &= (~HC_EVENT_EXIT);
+ pthread_mutex_unlock(&hci->event_lock);
+
+ ALOGD("events = 0x%x", events);
+ if (events & HC_EVENT_EXIT) {
+ ALOGD("Got Exit event.. Exiting HCI");
+ fm_hci_exit(hci);
+ break;
+ }
+ }
+ ALOGV("%s--", __func__);
+ return NULL;
+}
+
+static int start_mon_thread(struct fm_hci_t *hci)
+{
+ int ret = FM_HC_STATUS_SUCCESS;
+ ALOGD("%s", __func__);
+ if ((ret = pthread_create(&hci->mon_thread, NULL,
+ hci_mon_thread, hci)) !=0) {
+ ALOGE("pthread_create failed! status = %d (%s)",
+ ret, strerror(ret));
+ lib_running = 0;
+ }
+ return ret;
+}
+
+static void stop_mon_thread(struct fm_hci_t *hci)
+{
+ int ret;
+ ALOGV("%s++", __func__);
+ if ((ret = pthread_kill(hci->mon_thread, SIGUSR1))
+ == FM_HC_STATUS_SUCCESS) {
+ ALOGE("%s:pthread_join", __func__);
+ if ((ret = pthread_join(hci->mon_thread, NULL)) != FM_HC_STATUS_SUCCESS)
+ ALOGE("Error joining mon thread, error = %d (%s)",
+ ret, strerror(ret));
+ } else {
+ ALOGE("Error killing mon thread, error = %d (%s)",
+ ret, strerror(ret));
+ }
+}
+
+static int start_rx_thread(struct fm_hci_t *hci)
+{
+ int ret = FM_HC_STATUS_SUCCESS;
+ ALOGV("%s++", __func__);
+
+ ALOGD("%s: Starting the userial read thread....", __func__);
+ if ((ret = pthread_create(&hci->rx_thread, NULL, \
+ hci_read_thread, hci)) != 0) {
+ ALOGE("pthread_create failed! status = %d (%s)",
+ ret, strerror(ret));
+ lib_running = 0;
+ }
+ return ret;
+}
+
+static void stop_rx_thread(struct fm_hci_t *hci)
+{
+ int ret;
+ ALOGV("%s++", __func__);
+ if ((ret = pthread_kill(hci->rx_thread, SIGUSR1))
+ == FM_HC_STATUS_SUCCESS) {
+ ALOGE("%s:pthread_join", __func__);
+ if ((ret = pthread_join(hci->rx_thread, NULL)) != FM_HC_STATUS_SUCCESS)
+ ALOGE("Error joining rx thread, error = %d (%s)",
+ ret, strerror(ret));
+ } else {
+ ALOGE("Error killing rx thread, error = %d (%s)",
+ ret, strerror(ret));
+ }
+}
+
+static int power(struct fm_hci_t *hci, fm_power_state_t state)
+{
+ int i,opcode,ret;
+ int init_success = 0;
+ char value[PROPERTY_VALUE_MAX] = {'\0'};
+ if (fm_hal_fd)
+ {
+ if (state)
+ opcode = 2;
+ else {
+ opcode = 1;
+ }
+ ALOGI("%s:opcode: %x", LOG_TAG, opcode);
+ ret = write(fm_hal_fd,&opcode, 1);
+ if (ret < 0) {
+ ALOGE("failed to write fm hal socket");
+ } else {
+ ret = FM_HC_STATUS_SUCCESS;
+ }
+ } else {
+ ALOGE("Connect to socket failed ..");
+ ret = -1;
+ }
+ if (state == FM_RADIO_DISABLE) {
+ for (i=0; i<10; i++) {
+ property_get("wc_transport.fm_power_status", value, "0");
+ if (strcmp(value, "0") == 0) {
+ init_success = 1;
+ break;
+ } else {
+ usleep(WAIT_TIMEOUT);
+ }
+ }
+ ALOGI("fm power OFF status:%d after %f seconds \n", init_success, 0.2*i);
+ stop_fmhal_service();
+ }
+ if (state == FM_RADIO_ENABLE) {
+ for (i=0; i<10; i++) {
+ property_get("wc_transport.fm_power_status", value, "0");
+ if (strcmp(value, "1") == 0) {
+ init_success = 1;
+ break;
+ } else {
+ usleep(WAIT_TIMEOUT);
+ }
+ }
+ ALOGI("fm power ON status:%d after %f seconds \n", init_success, 0.2*i);
+ }
+ return ret;
+}
+
+#define CH_MAX 3
+static int serial_port_init(struct fm_hci_t *hci)
+{
+ int i, ret;
+ int fd_array[CH_MAX];
+
+ for (int i = 0; i < CH_MAX; i++)
+ fd_array[i] = -1;
+
+ ALOGI("%s: Opening the TTy Serial port...", __func__);
+ ret = hci->vendor->op(BT_VND_OP_FM_USERIAL_OPEN, &fd_array);
+
+ if (fd_array[0] == -1) {
+ ALOGE("%s unable to open TTY serial port", __func__);
+ goto err;
+ }
+ hci->fd = fd_array[0];
+
+ return FM_HC_STATUS_SUCCESS;
+
+err:
+ return FM_HC_STATUS_FAIL;
+}
+
+static void serial_port_close(struct fm_hci_t *hci)
+{
+ //TODO: what if hci/fm_vnd_if is null.. need to take lock and check
+ ALOGI("%s: Closing the TTy Serial port!!!", __func__);
+ hci->vendor->op(BT_VND_OP_FM_USERIAL_CLOSE, NULL);
+ hci->fd = -1;
+}
+
+static int enqueue_fm_tx_cmd(struct fm_hci_t *hci, struct fm_command_header_t *pbuf)
+{
+ struct transmit_queue_t *element = (struct transmit_queue_t *) malloc(sizeof(struct transmit_queue_t));
+
+ if (!element) {
+ ALOGI("Failed to allocate memory for element!!\n");
+ return FM_HC_STATUS_NOMEM;
+ }
+ element->hdr = pbuf;
+ element->next = NULL;
+
+ pthread_mutex_lock(&hci->tx_q_lock);
+
+ if (!hci->first) {
+ hci->last = hci->first = element;
+ } else {
+ hci->last->next = element;
+ hci->last = element;
+ }
+ ALOGI("%s: FM-CMD ENQUEUED SUCCESSFULLY", __func__);
+
+ pthread_mutex_unlock(&hci->tx_q_lock);
+
+ return FM_HC_STATUS_SUCCESS;
+}
+
+int fm_hci_transmit(void *hci, struct fm_command_header_t *buf)
+{
+ int status = FM_HC_STATUS_FAIL;
+
+ if (!hci || !buf) {
+ ALOGE("NULL input arguments");
+ return FM_HC_STATUS_NULL_POINTER;
+ }
+
+ if ((status = enqueue_fm_tx_cmd((struct fm_hci_t *)hci, buf))
+ == FM_HC_STATUS_SUCCESS)
+ event_notification(hci, HC_EVENT_TX);
+
+ return status;
+}
+
+void fm_hci_close(void *arg) {
+
+ ALOGV("%s close fm userial ", __func__);
+
+ struct fm_hci_t *hci = (struct fm_hci_t *)arg;
+ if (!hci) {
+ ALOGE("NULL arguments");
+ return;
+ }
+ event_notification(hci, HC_EVENT_EXIT);
+ pthread_mutex_lock(&hci->event_lock);
+again:
+ pthread_cond_wait(&hci->event_cond, &hci->event_lock);
+ if (!(ready_events & HC_EVENT_EXIT_DONE))
+ goto again;
+ pthread_mutex_unlock(&hci->event_lock);
+}
+
+int fm_hci_init(fm_hci_hal_t *hci_hal)
+{
+ int ret = FM_HC_STATUS_FAIL;
+ struct fm_hci_t *hci = NULL;
+ ALOGV("++%s", __func__);
+
+ if (!hci_hal || !hci_hal->hal) {
+ ALOGE("NULL input argument");
+ return FM_HC_STATUS_NULL_POINTER;
+ }
+
+ hci = malloc(sizeof(struct fm_hci_t));
+ if (!hci) {
+ ALOGE("Failed to malloc hci context");
+ return FM_HC_STATUS_NOMEM;
+ }
+ memset(hci, 0, sizeof(struct fm_hci_t));
+
+ pthread_mutex_init(&hci->tx_q_lock, NULL);
+ pthread_mutex_init(&hci->credit_lock, NULL);
+ pthread_mutex_init(&hci->event_lock, NULL);
+
+ pthread_cond_init(&hci->event_cond, NULL);
+ pthread_cond_init(&hci->cmd_credits_cond, NULL);
+
+ start_fmhal_service();
+ fm_hal_fd = connect_to_local_fmsocket("fmhal_sock");
+ if (fm_hal_fd == -1) {
+ ALOGI("FM hal service socket connect failed..");
+ goto err_socket;
+ }
+ ALOGI("fm_hal_fd = %d", fm_hal_fd);
+
+ lib_running = 1;
+ ready_events = 0;
+ hci->command_credits = 1;
+ hci->fd = -1;
+
+ ret = vendor_init(hci);
+ if (ret)
+ goto err_vendor;
+ ret = power(hci, FM_RADIO_ENABLE);
+ if (ret)
+ goto err_power;
+ ret = serial_port_init(hci);
+ if (ret)
+ goto err_serial;
+ ret = start_mon_thread(hci);
+ if (ret)
+ goto err_thread_mon;
+ ret = start_tx_thread(hci);
+ if (ret)
+ goto err_thread_tx;
+ ret = start_rx_thread(hci);
+ if (ret)
+ goto err_thread_rx;
+
+ hci->cb = hci_hal->cb;
+ hci->private_data = hci_hal->hal;
+ hci_hal->hci = hci;
+ ALOGD("--%s success", __func__);
+ return FM_HC_STATUS_SUCCESS;
+
+err_thread_rx:
+ stop_rx_thread(hci);
+err_thread_tx:
+ stop_tx_thread(hci);
+err_thread_mon:
+ stop_mon_thread(hci);
+err_serial:
+ serial_port_close(hci);
+err_power:
+ power(hci, FM_RADIO_DISABLE);
+err_vendor:
+ vendor_close(hci);
+err_socket:
+ stop_fmhal_service();
+
+ pthread_mutex_destroy(&hci->tx_q_lock);
+ pthread_mutex_destroy(&hci->credit_lock);
+ pthread_mutex_destroy(&hci->event_lock);
+ pthread_cond_destroy(&hci->event_cond);
+ pthread_cond_destroy(&hci->cmd_credits_cond);
+
+ lib_running = 0;
+ ready_events = 0;
+ hci->command_credits = 0;
+ free(hci);
+
+ ALOGE("--%s fail", __func__);
+ return ret;
+}
+
+static void fm_hci_exit(void *arg)
+{
+ struct fm_hci_t *hci = (struct fm_hci_t *)arg;
+ ALOGE("%s", __func__);
+
+ lib_running = 0;
+ ready_events = HC_EVENT_EXIT;
+ hci->command_credits = 0;
+ serial_port_close(hci);
+ power(hci, FM_RADIO_DISABLE);//need to address this
+ vendor_close(hci);
+ pthread_cond_broadcast(&hci->event_cond);
+ pthread_cond_broadcast(&hci->cmd_credits_cond);
+ event_notification(hci, HC_EVENT_EXIT_DONE);
+ stop_rx_thread(hci);
+ stop_tx_thread(hci);
+ ALOGD("Tx, Rx Threads join done");
+ pthread_mutex_destroy(&hci->tx_q_lock);
+ pthread_mutex_destroy(&hci->credit_lock);
+ pthread_mutex_destroy(&hci->event_lock);
+ pthread_cond_destroy(&hci->event_cond);
+ pthread_cond_destroy(&hci->cmd_credits_cond);
+
+ free(hci);
+ hci = NULL;
+}
+
diff --git a/fm_hci/fm_hci.cpp b/fm_hci/fm_hci.cpp
index c08b884..550b8dd 100644
--- a/fm_hci/fm_hci.cpp
+++ b/fm_hci/fm_hci.cpp
@@ -60,7 +60,6 @@
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::hardware::hidl_vec;
-using ::android::hardware::hidl_death_recipient;
static struct fm_hci_t hci;
@@ -82,31 +81,6 @@
static bool hci_initialize();
static void hci_transmit(struct fm_command_header_t *hdr);
static void hci_close();
-#define HCI_EV_HW_ERR_EVENT 0x1A
-
-void hal_service_died() {
- struct fm_event_header_t *temp = (struct fm_event_header_t *)
- malloc(sizeof(struct fm_event_header_t));
- if (temp != nullptr) {
- temp->evt_code = HCI_EV_HW_ERR_EVENT;
- temp->evt_len = 0;
- ALOGI("%s: evt_code: 0x%x", __func__, temp->evt_code);
- enqueue_fm_rx_event(temp);
- } else {
- ALOGE("%s: Memory Allocation failed for event buffer ",__func__);
- }
-}
-
-class FmHciDeathRecipient : public hidl_death_recipient {
- public:
- virtual void serviceDied(uint64_t /*cookie*/,
- const android::wp<::android::hidl::base::V1_0::IBase>& /*who*/) {
- ALOGE("Fm HAL service died!");
- hal_service_died();
- }
-};
-
-android::sp<FmHciDeathRecipient> fmHciDeathRecipient = new FmHciDeathRecipient();
/*******************************************************************************
**
@@ -511,7 +485,6 @@
int ret;
ALOGI("++%s: is_hci_initialize: %d", __func__, is_hci_initialize);
- hci.on_mtx.lock();
while (is_hci_initialize) {
ret = start_tx_thread();
if (ret)
@@ -534,7 +507,6 @@
}
hci.on_cond.notify_all();
- hci.on_mtx.unlock();
ALOGI("--%s: is_hci_initialize: %d", __func__, is_hci_initialize);
}
@@ -602,6 +574,8 @@
ALOGI("%s: acquiring mutex", __func__);
std::lock_guard<std::recursive_mutex> lk(mtx);
+ fmHci = IFmHci::getService();
+
if (fmHci != nullptr) {
hci.state = FM_RADIO_ENABLING;
android::sp<IFmHciCallbacks> callbacks = new FmHciCallbacks();
@@ -666,10 +640,6 @@
std::lock_guard<std::recursive_mutex> lk(mtx);
if (fmHci != nullptr) {
- auto death_unlink = fmHci->unlinkToDeath(fmHciDeathRecipient);
- if (!death_unlink.isOk()) {
- ALOGE( "%s: Error unlinking death recipient from the Fm HAL", __func__);
- }
auto hidl_daemon_status = fmHci->close();
if(!hidl_daemon_status.isOk()) {
ALOGE("%s: HIDL daemon is dead", __func__);
@@ -730,10 +700,10 @@
if (hci_initialize()) {
//wait for iniialization complete
- Lock lk(hci.on_mtx);
+ ALOGD("--%s waiting for iniialization complete hci state: %d ",
+ __func__, hci.state);
if(hci.state == FM_RADIO_ENABLING){
- ALOGD("--%s waiting for iniialization complete hci state: %d ",
- __func__, hci.state);
+ Lock lk(hci.on_mtx);
std::cv_status status = std::cv_status::no_timeout;
auto now = std::chrono::system_clock::now();
status =
@@ -744,7 +714,6 @@
kill(getpid(), SIGKILL);
}
}
- hci.on_mtx.unlock();
}
if (hci.state == FM_RADIO_ENABLED) {
diff --git a/fm_hci/wcnss_hci.h b/fm_hci/wcnss_hci.h
new file mode 100644
index 0000000..95fa9c1
--- /dev/null
+++ b/fm_hci/wcnss_hci.h
@@ -0,0 +1,70 @@
+/*
+ * 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 __WCNSS_HCI__
+#define __WCNSS_HCI__
+static void vendor_fwcfg_cb(bt_vendor_op_result_t result) {
+}
+
+static void vendor_scocfg_cb(bt_vendor_op_result_t result) {
+}
+
+static void vendor_lpm_vnd_cb(bt_vendor_op_result_t result) {
+}
+
+static void sco_audiostate_cb(bt_vendor_op_result_t result) {
+}
+
+static void* vendor_alloc(int size) {
+ return NULL;
+}
+
+static void vendor_dealloc(void *p_buf) {
+}
+
+static uint8_t vendor_xmit_cb(uint16_t opcode, void *p_buf, tINT_CMD_CBACK p_cback) {
+ return 0;
+}
+
+static void vendor_epilog_cb(bt_vendor_op_result_t result) {
+}
+
+
+static const bt_vendor_callbacks_t fm_vendor_callbacks = {
+ sizeof(fm_vendor_callbacks),
+ vendor_fwcfg_cb,
+ vendor_scocfg_cb,
+ vendor_lpm_vnd_cb,
+ sco_audiostate_cb,
+ vendor_alloc,
+ vendor_dealloc,
+ vendor_xmit_cb,
+ vendor_epilog_cb
+};
+#endif
diff --git a/fmapp2/Android.bp b/fmapp2/Android.bp
index a2ef6ab..eeeab5f 100755
--- a/fmapp2/Android.bp
+++ b/fmapp2/Android.bp
@@ -3,7 +3,7 @@
android_app {
name: "FM2",
- srcs: ["src/com/caf/fmradio/CommaSeparatedFreqFileReader.java"] + ["src/com/caf/fmradio/FMAdapterApp.java"] + ["src/com/caf/fmradio/FMMediaButtonIntentReceiver.java"] + ["src/com/caf/fmradio/FMRadio.java"] + ["src/com/caf/fmradio/FMRadioService.java"] + ["src/com/caf/fmradio/FmSharedPreferences.java"] + ["src/com/caf/fmradio/FMStats.java"] + ["src/com/caf/fmradio/FmTags.java"] + ["src/com/caf/fmradio/GetNextFreqInterface.java"] + ["src/com/caf/fmradio/HorizontalNumberPicker.java"] + ["src/com/caf/fmradio/PresetList.java"] + ["src/com/caf/fmradio/PresetStation.java"] + ["src/com/caf/fmradio/Settings.java"] + ["src/com/caf/fmradio/StationListActivity.java"] + ["src/com/caf/fmradio/IFMRadioService.aidl"] + ["src/com/caf/fmradio/IFMRadioServiceCallbacks.aidl"] + ["src/com/caf/fmradio/IFMTransmitterServiceCallbacks.aidl"] + ["src/com/caf/hc_utils/**/*.java"],
+ srcs: ["src/com/caf/fmradio/CommaSeparatedFreqFileReader.java"] + ["src/com/caf/fmradio/FMAdapterApp.java"] + ["src/com/caf/fmradio/FMMediaButtonIntentReceiver.java"] + ["src/com/caf/fmradio/FMRadio.java"] + ["src/com/caf/fmradio/FMRadioService.java"] + ["src/com/caf/fmradio/FmSharedPreferences.java"] + ["src/com/caf/fmradio/FMStats.java"] + ["src/com/caf/fmradio/FmTags.java"] + ["src/com/caf/fmradio/GetNextFreqInterface.java"] + ["src/com/caf/fmradio/HorizontalNumberPicker.java"] + ["src/com/caf/fmradio/PresetList.java"] + ["src/com/caf/fmradio/PresetStation.java"] + ["src/com/caf/fmradio/Settings.java"] + ["src/com/caf/fmradio/StationListActivity.java"] + ["src/com/caf/fmradio/IFMRadioService.aidl"] + ["src/com/caf/fmradio/IFMRadioServiceCallbacks.aidl"] + ["src/com/caf/hc_utils/**/*.java"],
certificate: "platform",
jni_libs: ["libqcomfm_jni"],
libs: ["qcom.fmradio"],
diff --git a/fmapp2/src/com/caf/fmradio/FMRadioService.java b/fmapp2/src/com/caf/fmradio/FMRadioService.java
index 7b99141..1c7c700 100644
--- a/fmapp2/src/com/caf/fmradio/FMRadioService.java
+++ b/fmapp2/src/com/caf/fmradio/FMRadioService.java
@@ -851,7 +851,9 @@
setLowPowerMode(false);
if(false == mPlaybackInProgress) {
startFM();
- enableSlimbus(ENABLE_SLIMBUS_DATA_PORT);
+ if (mReceiver.isCherokeeChip() && (mPref.getBoolean("SLIMBUS_SEQ", true))) {
+ enableSlimbus(ENABLE_SLIMBUS_DATA_PORT);
+ }
}
}
}
@@ -1607,9 +1609,10 @@
//intentional fall through.
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
Log.v(LOGTAG, "AudioFocus: received AUDIOFOCUS_LOSS_TRANSIENT");
-
- enableSlimbus(DISABLE_SLIMBUS_DATA_PORT);
-
+ if (mReceiver != null && mReceiver.isCherokeeChip() &&
+ (mPref.getBoolean("SLIMBUS_SEQ", true))) {
+ enableSlimbus(DISABLE_SLIMBUS_DATA_PORT);
+ }
if (true == mPlaybackInProgress) {
stopFM();
}
@@ -1631,8 +1634,10 @@
if(false == mPlaybackInProgress) {
startFM();
- enableSlimbus(ENABLE_SLIMBUS_DATA_PORT);
-
+ if (mReceiver != null && mReceiver.isCherokeeChip() &&
+ (mPref.getBoolean("SLIMBUS_SEQ", true))) {
+ enableSlimbus(ENABLE_SLIMBUS_DATA_PORT);
+ }
} else {
/* This case usually happens, when FM volume is lowered down and Playback
* In Progress on AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK recived. Need
@@ -1943,6 +1948,11 @@
{
return(mService.get().getProgramID());
}
+ public int[] getSearchList()
+ {
+ return(mService.get().getSearchList());
+ }
+
public boolean setLowPowerMode(boolean enable)
{
return(mService.get().setLowPowerMode(enable));
@@ -2187,6 +2197,86 @@
return status;
}
+ private boolean fmTurnOnSequence () {
+ boolean bStatus = false;
+ // This sets up the FM radio device
+ FmConfig config = FmSharedPreferences.getFMConfiguration();
+
+ Log.d(LOGTAG, "fmOn: RadioBand :"+ config.getRadioBand());
+ Log.d(LOGTAG, "fmOn: Emphasis :"+ config.getEmphasis());
+ Log.d(LOGTAG, "fmOn: ChSpacing :"+ config.getChSpacing());
+ Log.d(LOGTAG, "fmOn: RdsStd :"+ config.getRdsStd());
+ Log.d(LOGTAG, "fmOn: LowerLimit :"+ config.getLowerLimit());
+ Log.d(LOGTAG, "fmOn: UpperLimit :"+ config.getUpperLimit());
+
+ mEventReceived = false;
+ bStatus = mReceiver.enable(FmSharedPreferences.getFMConfiguration(), this);
+
+ if (mReceiver.isCherokeeChip()) {
+ bStatus = waitForEvent();
+ }
+
+ Log.d(LOGTAG, "mReceiver.enable done, Status :" + bStatus);
+
+ if (bStatus == true)
+ {
+ /* Put the hardware into normal mode */
+ bStatus = setLowPowerMode(false);
+ Log.d(LOGTAG, "setLowPowerMode done, Status :" + bStatus);
+
+
+ AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+ if( (audioManager != null) &&(false == mPlaybackInProgress) )
+ {
+ Log.d(LOGTAG, "mAudioManager.setFmRadioOn = true \n" );
+ //audioManager.setParameters("FMRadioOn="+mAudioDevice);
+ int state = getCallState();
+ if ( TelephonyManager.CALL_STATE_IDLE != getCallState() )
+ {
+ fmActionOnCallState(state);
+ } else {
+ startFM(); // enable FM Audio only when Call is IDLE
+ }
+ Log.d(LOGTAG, "mAudioManager.setFmRadioOn done \n" );
+ }
+ if (mReceiver != null) {
+ bStatus = mReceiver.registerRdsGroupProcessing(FmReceiver.FM_RX_RDS_GRP_RT_EBL|
+ FmReceiver.FM_RX_RDS_GRP_PS_EBL|
+ FmReceiver.FM_RX_RDS_GRP_AF_EBL|
+ FmReceiver.FM_RX_RDS_GRP_PS_SIMPLE_EBL|
+ FmReceiver.FM_RX_RDS_GRP_ECC_EBL|
+ FmReceiver.FM_RX_RDS_GRP_PTYN_EBL|
+ FmReceiver.FM_RX_RDS_GRP_RT_PLUS_EBL);
+ Log.d(LOGTAG, "registerRdsGroupProcessing done, Status :" + bStatus);
+ }
+ bStatus = enableAutoAF(FmSharedPreferences.getAutoAFSwitch());
+ Log.d(LOGTAG, "enableAutoAF done, Status :" + bStatus);
+
+ /* There is no internal Antenna*/
+ bStatus = mReceiver.setInternalAntenna(false);
+ Log.d(LOGTAG, "setInternalAntenna done, Status :" + bStatus);
+
+ /* Read back to verify the internal Antenna mode*/
+ readInternalAntennaAvailable();
+
+ startNotification();
+ bStatus = true;
+ }
+ else
+ {
+ if ((mReceiver.getFMState() != mReceiver.subPwrLevel_FMRx_Starting) &&
+ (mReceiver.getFMState() != mReceiver.FMState_Rx_Turned_On)) {
+ mReceiver = null; // as enable failed no need to disable
+ // failure of enable can be because handle
+ // already open which gets effected if
+ // we disable
+ stop();
+ }
+ }
+
+ return bStatus;
+ }
+
private boolean enableSlimbus(int flag) {
Log.d(LOGTAG, "enableSlimbus");
boolean bStatus = false;
@@ -2205,7 +2295,7 @@
* .
* @return true if fm Enable api was invoked successfully, false if the api failed.
*/
- private boolean fmTurnOnSequence() {
+ private boolean fmTurnOnSequenceCherokee () {
boolean bStatus = false;
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
if ((audioManager != null) & (false == mPlaybackInProgress)) {
@@ -2311,9 +2401,15 @@
}
else
{
- enableSlimbus(ENABLE_SLIMBUS_DATA_PORT);
- bStatus = fmTurnOnSequence();
- /* reset SSR flag */
+ if (mReceiver.isCherokeeChip()) {
+ if (mPref.getBoolean("SLIMBUS_SEQ", true)) {
+ enableSlimbus(ENABLE_SLIMBUS_DATA_PORT);
+ }
+ bStatus = fmTurnOnSequenceCherokee();
+ } else {
+ bStatus = fmTurnOnSequence();
+ }
+ /* reset SSR flag */
mIsSSRInProgressFromActivity = false;
}
}
@@ -2386,6 +2482,23 @@
private boolean fmOffImpl() {
boolean bStatus=false;
+ // This will disable the FM radio device
+ synchronized(mReceiverLock) {
+ if (mReceiver != null)
+ {
+ bStatus = mReceiver.disable(this);
+ mReceiver = null;
+ }
+ }
+ fmOperationsOff();
+ stop();
+
+ return(bStatus);
+ }
+
+ private boolean fmOffImplCherokee() {
+ boolean bStatus=false;
+
fmOperationsOff();
stop();
try {
@@ -2421,7 +2534,11 @@
private boolean fmOff() {
boolean ret = false;
if (mReceiver != null) {
- ret = fmOffImpl();
+ if (mReceiver.isCherokeeChip()) {
+ ret = fmOffImplCherokee();
+ } else {
+ ret = fmOffImpl();
+ }
}
mWakeLock.release();
return ret;
@@ -2505,7 +2622,9 @@
return;
mSpeakerPhoneOn = speakerOn;
- enableSlimbus(DISABLE_SLIMBUS_DATA_PORT);
+ if (mReceiver.isCherokeeChip() && (mPref.getBoolean("SLIMBUS_SEQ", true))) {
+ enableSlimbus(DISABLE_SLIMBUS_DATA_PORT);
+ }
if (speakerOn == false) {
mAudioDevice = AudioDeviceInfo.TYPE_WIRED_HEADPHONES;
@@ -2519,7 +2638,9 @@
String keyValPairs = new String("fm_routing="+mAudioDeviceType);
Log.d(LOGTAG, "keyValPairs = "+keyValPairs);
audioManager.setParameters(keyValPairs);
- enableSlimbus(ENABLE_SLIMBUS_DATA_PORT);
+ if (mReceiver.isCherokeeChip() && (mPref.getBoolean("SLIMBUS_SEQ", true))) {
+ enableSlimbus(ENABLE_SLIMBUS_DATA_PORT);
+ }
}
/*
* ReConfigure the FM Setup parameters
@@ -2915,6 +3036,24 @@
return pi;
}
+
+ /* Retrieves the station list from the SearchStationlist.
+ *
+ * @return Array of integers that represents the station frequencies.
+ * Note: 1. This is a synchronous call that should typically called when
+ * Callback onSearchListComplete.
+ */
+ public int[] getSearchList()
+ {
+ int[] frequencyList = null;
+ if (mReceiver != null)
+ {
+ Log.d(LOGTAG, "getSearchList: ");
+ frequencyList = mReceiver.getStationList();
+ }
+ return frequencyList;
+ }
+
/* Set the FM Power Mode on the FM hardware SoC.
* Typically used when UI/Activity is in the background, so the Host is interrupted less often.
*
@@ -3108,9 +3247,11 @@
{
Log.d(LOGTAG, "FmRxEvEnableReceiver");
if (mReceiver != null) {
- synchronized(mEventWaitLock) {
- mEventReceived = true;
- mEventWaitLock.notify();
+ if (mReceiver.isCherokeeChip()) {
+ synchronized(mEventWaitLock) {
+ mEventReceived = true;
+ mEventWaitLock.notify();
+ }
}
}
}
@@ -3119,9 +3260,11 @@
Log.d(LOGTAG, "FmRxEvDisableReceiver");
mFMOn = false;
FmSharedPreferences.clearTags();
- synchronized (mEventWaitLock) {
- mEventReceived = true;
- mEventWaitLock.notify();
+ if (mReceiver != null && mReceiver.isCherokeeChip()) {
+ synchronized (mEventWaitLock) {
+ mEventReceived = true;
+ mEventWaitLock.notify();
+ }
}
}
public void FmRxEvRadioReset()
@@ -3357,9 +3500,11 @@
if (mCallbacks != null) {
try {
mCallbacks.getStationParamCb(val, status);
- synchronized(mEventWaitLock) {
- mEventReceived = true;
- mEventWaitLock.notify();
+ if (mReceiver != null && mReceiver.isCherokeeChip()) {
+ synchronized(mEventWaitLock) {
+ mEventReceived = true;
+ mEventWaitLock.notify();
+ }
}
} catch (RemoteException e) {
e.printStackTrace();
@@ -3568,18 +3713,21 @@
public void FmRxEvEnableSlimbus(int status)
{
Log.e(LOGTAG, "FmRxEvEnableSlimbus status = " + status);
- synchronized(mEventWaitLock) {
- mEventReceived = true;
- mEventWaitLock.notify();
+ if (mReceiver != null && mReceiver.isCherokeeChip()) {
+ synchronized(mEventWaitLock) {
+ mEventReceived = true;
+ mEventWaitLock.notify();
+ }
}
-
}
public void FmRxEvEnableSoftMute(int status)
{
Log.e(LOGTAG, "FmRxEvEnableSoftMute status = " + status);
- synchronized(mEventWaitLock) {
- mEventReceived = true;
- mEventWaitLock.notify();
+ if (mReceiver != null && mReceiver.isCherokeeChip()) {
+ synchronized(mEventWaitLock) {
+ mEventReceived = true;
+ mEventWaitLock.notify();
+ }
}
}
};
@@ -3865,7 +4013,18 @@
}
private void requestFocusImpl() {
- Log.d(LOGTAG, "++requestFocusImpl mPlaybackInProgress: " +
+ if( (false == mPlaybackInProgress) &&
+ (true == mStoppedOnFocusLoss) && isFmOn()) {
+ // adding code for audio focus gain.
+ AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+ audioManager.requestAudioFocus(mGainFocusReq);
+ startFM();
+ mStoppedOnFocusLoss = false;
+ }
+ }
+
+ private void requestFocusImplCherokee() {
+ Log.d(LOGTAG, "++requestFocusImplCherokee mPlaybackInProgress: " +
mPlaybackInProgress + " mStoppedOnFocusLoss: " +
mStoppedOnFocusLoss + " isFmOn: " + isFmOn());
if( (false == mPlaybackInProgress) &&
@@ -3875,7 +4034,9 @@
audioManager.requestAudioFocus(mGainFocusReq);
if(false == mPlaybackInProgress) {
startFM();
- enableSlimbus(ENABLE_SLIMBUS_DATA_PORT);
+ if (mReceiver.isCherokeeChip() && (mPref.getBoolean("SLIMBUS_SEQ", true))) {
+ enableSlimbus(ENABLE_SLIMBUS_DATA_PORT);
+ }
}
mStoppedOnFocusLoss = false;
}
@@ -3883,10 +4044,15 @@
private void requestFocus() {
Log.d(LOGTAG, "++requestFocus");
- requestFocusImpl();
+ if (mReceiver.isCherokeeChip() && (mPref.getBoolean("SLIMBUS_SEQ", true))) {
+ requestFocusImplCherokee();
+ } else {
+ requestFocusImpl();
+ }
Log.d(LOGTAG, "--requestFocus");
}
+
public void onAudioFocusChange(int focusChange) {
mDelayedStopHandler.obtainMessage(FOCUSCHANGE, focusChange, 0).sendToTarget();
}
diff --git a/fmapp2/src/com/caf/fmradio/FMStats.java b/fmapp2/src/com/caf/fmradio/FMStats.java
index 063326d..71a4a38 100644
--- a/fmapp2/src/com/caf/fmradio/FMStats.java
+++ b/fmapp2/src/com/caf/fmradio/FMStats.java
@@ -3018,11 +3018,15 @@
case SEARCH_TEST:
try {
Log.e(LOGTAG, "start scanning\n");
- Log.d(LOGTAG,"Scanning with 0 scan time");
- if (mReceiver != null)
- mIsSearching = mReceiver.searchStations(FmReceiver.FM_RX_SRCH_MODE_SCAN,
- SCAN_DWELL_PERIOD, FmReceiver.FM_RX_SEARCHDIR_UP);
- } catch (Exception e) {
+ if(isTransportLayerSMD() || isCherokeeChip()) {
+ Log.d(LOGTAG,"Scanning with 0 scan time");
+ if (mReceiver != null)
+ mIsSearching = mReceiver.searchStations(FmReceiver.FM_RX_SRCH_MODE_SCAN,
+ SCAN_DWELL_PERIOD, FmReceiver.FM_RX_SEARCHDIR_UP);
+ } else {
+ mIsSearching = mService.scan(0);
+ }
+ }catch (RemoteException e) {
e.printStackTrace();
}
@@ -3219,12 +3223,16 @@
boolean isCherokeeChip = isCherokeeChip();
if((null != mService)) {
try {
- lastCmdSent = CMD_STNPARAM_RSSI;
- ret = mService.getRssi();
- if (ret != 0) {
- Log.e(LOGTAG, "getrssi cmd failed: ret = " + ret);
- lastCmdSent = 0;
- return null;
+ if (isCherokeeChip) {
+ lastCmdSent = CMD_STNPARAM_RSSI;
+ ret = mService.getRssi();
+ if (ret != 0) {
+ Log.e(LOGTAG, "getrssi cmd failed: ret = " + ret);
+ lastCmdSent = 0;
+ return null;
+ }
+ } else {
+ nRssi = mService.getRssi();
}
Log.e(LOGTAG, "Got response of mService.getRssi");
if (nRssi != Integer.MAX_VALUE) {
diff --git a/fmapp2/src/com/caf/fmradio/IFMRadioService.aidl b/fmapp2/src/com/caf/fmradio/IFMRadioService.aidl
index 70d906e..9766093 100644
--- a/fmapp2/src/com/caf/fmradio/IFMRadioService.aidl
+++ b/fmapp2/src/com/caf/fmradio/IFMRadioService.aidl
@@ -25,6 +25,7 @@
boolean scan(int pty);
boolean seekPI(int piCode);
boolean searchStrongStationList(int numStations);
+ int[] getSearchList();
boolean cancelSearch();
String getProgramService();
String getRadioText();
diff --git a/fmapp2/src/com/caf/fmradio/IFMTransmitterServiceCallbacks.aidl b/fmapp2/src/com/caf/fmradio/IFMTransmitterServiceCallbacks.aidl
deleted file mode 100644
index 641785d..0000000
--- a/fmapp2/src/com/caf/fmradio/IFMTransmitterServiceCallbacks.aidl
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2009,2012-2013 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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.
- */
-
-package com.caf.fmradio;
-
-interface IFMTransmitterServiceCallbacks
-{
- void onEnabled(boolean bStatus);
- void onDisabled();
- void onRadioReset();
- void onTuneStatusChanged(int frequency);
- void onRadioTextChanged();
- void onSearchListComplete(boolean bStatus);
- void onReconfigured();
- void onMetaDataChanged(String str);
- void onPSInfoSent(String str);
-}
diff --git a/helium/radio-helium.h b/helium/radio-helium.h
index d3dae68..eb6ea9b 100644
--- a/helium/radio-helium.h
+++ b/helium/radio-helium.h
@@ -134,7 +134,6 @@
#define FM_AFJUMP_CONFG_MODE 0x42
#define FM_SRCH_CNFG_LEN 0x08
#define FM_AFJUMP_CNFG_LEN 0x06
-#define STD_BUF_SIZE 256
/* HCI timeouts */
#define RADIO_HCI_TIMEOUT (10000) /* 10 seconds */
@@ -514,10 +513,6 @@
#define HCI_EV_RADIO_TEXT_PLUS_TAG 0x19
#define HCI_EV_HW_ERR_EVENT 0x1A
-/*HCI event opcode for fm driver RDS support*/
-#define HCI_EV_DRIVER_RDS_EVENT 0x1B
-#define HCI_EV_E_RADIO_TEXT 0x1C
-
#define HCI_REQ_DONE 0
#define HCI_REQ_PEND 1
#define HCI_REQ_CANCELED 2
diff --git a/helium/radio_helium_hal.c b/helium/radio_helium_hal.c
index e258bff..79e69f5 100644
--- a/helium/radio_helium_hal.c
+++ b/helium/radio_helium_hal.c
@@ -862,50 +862,6 @@
}
}
-static void hci_ev_driver_rds_event(uint8_t buff[])
-{
- uint8_t rds_type;
- char *rds_data = NULL;
- rds_type = buff[0];
-
- ALOGD("%s:%s:rds type = 0x%x", LOG_TAG, __func__, rds_type);
- rds_data = malloc(STD_BUF_SIZE);
- if (rds_data == NULL) {
- ALOGE("%s:memory allocation failed\n", LOG_TAG);
- return;
- } else {
- memcpy(rds_data, &buff[1],STD_BUF_SIZE);
- }
-
- switch (rds_type) {
- case HCI_EV_RADIO_TEXT:
- hal->jni_cb->rt_update_cb(rds_data);
- break;
-
- case HCI_EV_PROGRAM_SERVICE:
- hal->jni_cb->ps_update_cb(rds_data);
- break;
-
- case HCI_EV_FM_AF_LIST:
- hal->jni_cb->af_list_update_cb((uint16_t *)&rds_data);
- break;
-
- case HCI_EV_RADIO_TEXT_PLUS_TAG:
- hal->jni_cb->rt_plus_update_cb(rds_data);
- break;
-
- case HCI_EV_E_RADIO_TEXT:
- hal->jni_cb->ert_update_cb(rds_data);
- break;
-
- default:
- ALOGD("%s: Unknown RDS event", __func__);
- break;
- }
-
- free(rds_data);
-}
-
static void hci_ev_ert()
{
char *data = NULL;
@@ -1119,9 +1075,6 @@
case HCI_EV_EXT_COUNTRY_CODE:
hci_ev_ext_country_code(((struct fm_event_header_t *)evt_buf)->params);
break;
- case HCI_EV_DRIVER_RDS_EVENT:
- hci_ev_driver_rds_event(((struct fm_event_header_t *)evt_buf)->params);
- break;
case HCI_EV_HW_ERR_EVENT:
hci_ev_hw_error();
break;
diff --git a/jni/Android.bp b/jni/Android.bp
index 1d6e8e0..29da75c 100755
--- a/jni/Android.bp
+++ b/jni/Android.bp
@@ -4,6 +4,10 @@
system_ext_specific: true,
srcs: [
"android_hardware_fm.cpp",
+ "ConfFileParser.cpp",
+ "ConfigFmThs.cpp",
+ "FmIoctlsInterface.cpp",
+ "FmPerformanceParams.cpp",
],
host_ldlibs: ["-ldl"],
diff --git a/jni/ConfFileParser.cpp b/jni/ConfFileParser.cpp
new file mode 100644
index 0000000..d270690
--- /dev/null
+++ b/jni/ConfFileParser.cpp
@@ -0,0 +1,930 @@
+/*
+ * Copyright (c) 2014, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 "ConfFileParser.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <math.h>
+#include <utils/Log.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
+)
+{
+ const char *equal_start;
+ char *key = NULL;
+ char *val = NULL;
+ 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");
+ if(key){
+ free(key);
+ key = NULL;
+ }
+ 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 = 0;
+ unsigned val_len = 0;
+ 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");
+ goto err;
+ }
+ while((list != NULL) && strcmp(key, list->key)) {
+ list = list->next;
+ }
+ if(list != NULL) {
+ ALOGE("group already contains the key\n");
+ goto err;
+ }else{
+ list = alloc_key_value_pair();
+ if(list == NULL) {
+ ALOGE("add key value failed as could not alloc memory for key\
+ val pair\n");
+ goto err;
+ }
+ if(key) {
+ 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);
+ goto err;
+ }
+ if(val) {
+ val_len = strlen(val);
+ }
+ list->value = (char *)malloc(sizeof(char) *
+ (val_len + 1));
+ if(!list->value) {
+ free(list->key);
+ free(list);
+ goto err;
+ }
+ memcpy(list->key, key, key_len);
+ memcpy(list->value, val, val_len);
+ if (key) free((char*)key);
+ if (val) free((char*)val);
+ 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");
+ goto err;
+ }
+err:
+ if (key) free((char*)key);
+ if (val) free((char*)val);
+ return FALSE;
+}
diff --git a/jni/ConfFileParser.h b/jni/ConfFileParser.h
new file mode 100644
index 0000000..33b86ce
--- /dev/null
+++ b/jni/ConfFileParser.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2014, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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/jni/ConfigFmThs.cpp b/jni/ConfigFmThs.cpp
new file mode 100644
index 0000000..3845dfd
--- /dev/null
+++ b/jni/ConfigFmThs.cpp
@@ -0,0 +1,528 @@
+/*
+ * Copyright (c) 2014, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 "ConfigFmThs.h"
+#include "FmPerformanceParams.h"
+#include <utils/Log.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_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[2].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[1].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;
+ char *saveptr = NULL;
+ unsigned int freq;
+ unsigned int *freqs_new_arr;
+ unsigned int size = 0;
+ unsigned int len = 0;
+
+ next_freq = strtok_r(freqs, str, &saveptr);
+ 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_r(NULL, str, &saveptr);
+ }
+ return len;
+}
+
+unsigned int ConfigFmThs :: extract_comma_sep_sinrs
+(
+ char *sinrs,
+ signed char **sinrs_arr,
+ const char *str
+)
+{
+ char *next_sinr;
+ char *saveptr = NULL;
+ signed char *sinrs_new_arr;
+ unsigned int size = 0;
+ unsigned int len = 0;
+ signed char sinr;
+
+ next_sinr = strtok_r(sinrs, str, &saveptr);
+ 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_r(NULL, str, &saveptr);
+ }
+ 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;
+ }
+ }
+ grps++;
+ }
+ }else {
+ ALOGE("No of groups found is zero\n");
+ }
+ free_strs(grps_cpy);
+ }
+ free_key_file(keyfile);
+ keyfile = NULL;
+}
diff --git a/jni/ConfigFmThs.h b/jni/ConfigFmThs.h
new file mode 100644
index 0000000..0a791f7
--- /dev/null
+++ b/jni/ConfigFmThs.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2014, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 "FmConst.h"
+#include "ConfFileParser.h"
+
+#define MAX_GRPS 3
+#define MAX_SRCH_PARAMS 8
+#define MAX_AF_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
+
+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,
+};
+
+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},
+ {"HYBRIDSEARCHLIST", HYBRD_SRCH_LIST},
+ {"SEARCHTHRESHOLDS", SRCH_THS},
+};
+
+//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);
+ public:
+ ConfigFmThs();
+ ~ConfigFmThs();
+ void SetRxSearchAfThs(const char *file, UINT fd);
+};
+
+#endif //__CONFIG_FM_THS_H__
diff --git a/jni/FmConst.h b/jni/FmConst.h
new file mode 100644
index 0000000..e37160f
--- /dev/null
+++ b/jni/FmConst.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2014, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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__
+
+
+typedef unsigned int UINT;
+typedef unsigned long ULINT;
+
+//return related
+const int IOCTL_SUCC = 0;
+const int FM_SUCCESS = 0;
+const int FM_FAILURE = -1;
+const int PROP_SET_SUCC = 0;
+
+#define TUNE_MULT 16
+const UINT CAL_DATA_SIZE = 23;
+#define STD_BUF_SIZE 256
+
+const char *const FM_PERFORMANCE_PARAMS = "/etc/fm/fm_srch_af_th.conf";
+const char *const CALIB_DATA_NAME = "/data/misc/fm/Riva_fm_cal";
+
+#define V4L2_CTRL_CLASS_USER 0x00980000
+#define V4L2_CID_BASE (V4L2_CTRL_CLASS_USER | 0x900)
+#define V4L2_CID_AUDIO_MUTE (V4L2_CID_BASE + 9)
+const UINT SEARCH_DWELL_TIME = 2;
+const UINT SEEK_DWELL_TIME = 0;
+
+enum FM_AUDIO_PATH
+{
+ AUDIO_DIGITAL_PATH,
+ AUDIO_ANALOG_PATH,
+};
+
+enum FM_DEVICE
+{
+ FM_DEV_NONE,
+ FM_RX,
+ FM_TX,
+};
+
+enum BUFF_INDEXES
+{
+ STATION_LIST_IND,
+ EVENT_IND,
+ RT_IND,
+ PS_IND,
+ AF_LIST_IND = PS_IND + 2,
+ RT_PLUS_IND = 11,
+ ERT_IND,
+};
+
+enum SEARCH_MODE
+{
+ SEEK_MODE,
+ SCAN_MODE,
+};
+
+enum SEARCH_DIR
+{
+ SEARCH_DOWN,
+ SEARCH_UP,
+};
+
+enum AUDIO_MODE
+{
+ MONO,
+ STEREO,
+};
+
+//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_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_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,
+ HCI_FM_HELIUM_SET_SPURTABLE = 0x0098092D,
+ HCI_FM_HELIUM_GET_SPUR_TBL = 0x0098092E,
+ V4L2_CID_PRV_IRIS_FREQ,
+ V4L2_CID_PRV_IRIS_SEEK,
+ V4L2_CID_PRV_IRIS_UPPER_BAND,
+ V4L2_CID_PRV_IRIS_LOWER_BAND,
+ V4L2_CID_PRV_IRIS_AUDIO_MODE,
+ V4L2_CID_PRV_IRIS_RMSSI,
+
+ V4L2_CID_PRV_ENABLE_SLIMBUS = 0x00980940,
+};
+
+#endif
diff --git a/jni/FmIoctlsInterface.cpp b/jni/FmIoctlsInterface.cpp
new file mode 100644
index 0000000..640a90f
--- /dev/null
+++ b/jni/FmIoctlsInterface.cpp
@@ -0,0 +1,323 @@
+/*
+ * Copyright (c) 2014, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 <utils/Log.h>
+#include <cutils/properties.h>
+#include <sys/ioctl.h>
+#include <linux/videodev2.h>
+#include <math.h>
+
+char const * const FmIoctlsInterface::LOGTAG = "FmIoctlsInterface";
+
+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;
+
+ for(int i = 0; i < 3; i++) {
+ ret = ioctl(fd, VIDIOC_S_CTRL, &control);
+ if(ret < IOCTL_SUCC) {
+ ret = FM_FAILURE;
+ }else {
+ ret = FM_SUCCESS;
+ break;
+ }
+ }
+ return ret;
+}
+
+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};
+
+ memset(&v4l2_ctls, 0, sizeof(v4l2_ctls));
+ memset(&ext_ctl, 0, sizeof(ext_ctl));
+
+ 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);
+ ALOGE("%s: calibration file read failed\n", LOGTAG);
+ return FM_FAILURE;
+ }
+ fclose(cal_fp);
+ ext_ctl.id = V4L2_CID_PRV_SET_CALIBRATION;
+ 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) {
+ ALOGE("%s: ioctl call failed\n", LOGTAG);
+ return FM_FAILURE;
+ }else {
+ return FM_SUCCESS;
+ }
+ }else {
+ return FM_SUCCESS;
+ }
+}
+
+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_FAILURE;
+ }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);
+ 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);
+ 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/jni/FmIoctlsInterface.h b/jni/FmIoctlsInterface.h
new file mode 100644
index 0000000..eda28ee
--- /dev/null
+++ b/jni/FmIoctlsInterface.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2014, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 "FmConst.h"
+
+#include <linux/videodev2.h>
+
+class FmIoctlsInterface
+{
+ private:
+ static char const * const LOGTAG;
+ 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);
+};
+
+//char const *FmIoctlsInterface::LOGTAG = "FmIoctlsInterface";
+
+#endif //__FM_IOCTL_INTERFACE_H__
diff --git a/jni/FmPerformanceParams.cpp b/jni/FmPerformanceParams.cpp
new file mode 100644
index 0000000..bdd0a43
--- /dev/null
+++ b/jni/FmPerformanceParams.cpp
@@ -0,0 +1,422 @@
+/*
+ * Copyright (c) 2014, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 "FmPerformanceParams.h"
+#include "FmIoctlsInterface.h"
+
+#include <cstdio>
+#include <linux/videodev2.h>
+#include <utils/Log.h>
+
+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 * 3 + 3)];
+
+ if(data != NULL) {
+ data[size++] = 0x40;
+ data[size++] = ((n * 3) + 1);
+ data[size++] = n;
+ while((size < (n * 3 + 2)) && (freqs != NULL)
+ && (sinrs != NULL)) {
+ freq = (*freqs - 76000) / 50;
+ data[size++] = (freq & 0xff);
+ data[size++] = ((freq >> 8) & 0xff);
+ data[size++] = *sinrs;
+ freqs++;
+ sinrs++;
+ }
+ if(size == (n * 3 + 3)) {
+ 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;
+}
diff --git a/jni/FmPerformanceParams.h b/jni/FmPerformanceParams.h
new file mode 100644
index 0000000..fd9ee52
--- /dev/null
+++ b/jni/FmPerformanceParams.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2014, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 "FmConst.h"
+
+class FmPerformanceParams
+{
+ private:
+ public:
+ 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 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);
+};
+
+#endif //__FM_PERFORMANCE_PARAMS_H__
diff --git a/jni/android_hardware_fm.cpp b/jni/android_hardware_fm.cpp
index 76490ee..622d4e9 100644
--- a/jni/android_hardware_fm.cpp
+++ b/jni/android_hardware_fm.cpp
@@ -32,6 +32,8 @@
#include <nativehelper/JNIHelp.h>
#include <utils/Log.h>
#include "utils/misc.h"
+#include "FmIoctlsInterface.h"
+#include "ConfigFmThs.h"
#include <cutils/properties.h>
#include <fcntl.h>
#include <math.h>
@@ -45,9 +47,7 @@
#include <vector>
#include "radio-helium.h"
-typedef unsigned int UINT;
-typedef unsigned long ULINT;
-#define STD_BUF_SIZE 256
+#define RADIO "/dev/radio0"
#define FM_JNI_SUCCESS 0L
#define FM_JNI_FAILURE -1L
#define SEARCH_DOWN 0
@@ -74,68 +74,6 @@
#define MASK_PTY (0x0000001F)
#define MASK_TXREPCOUNT (0x0000000F)
-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_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_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,
- HCI_FM_HELIUM_SET_SPURTABLE = 0x0098092D,
- HCI_FM_HELIUM_GET_SPUR_TBL = 0x0098092E,
- V4L2_CID_PRV_IRIS_FREQ,
- V4L2_CID_PRV_IRIS_SEEK,
- V4L2_CID_PRV_IRIS_UPPER_BAND,
- V4L2_CID_PRV_IRIS_LOWER_BAND,
- V4L2_CID_PRV_IRIS_AUDIO_MODE,
- V4L2_CID_PRV_IRIS_RMSSI,
-
- V4L2_CID_PRV_ENABLE_SLIMBUS = 0x00980940,
-};
-
enum search_dir_t {
SEEK_UP,
SEEK_DN,
@@ -648,6 +586,13 @@
};
/* native interface */
+static bool is_soc_pronto() {
+ if(strcmp(soc_name, "pronto") == 0)
+ return true;
+ else
+ return false;
+}
+
static void get_property(int ptype, char *value)
{
std::vector<vendor_property_t> vPropList;
@@ -660,6 +605,83 @@
}
}
+static jint android_hardware_fmradio_FmReceiverJNI_acquireFdNative
+ (JNIEnv* env, jobject thiz, jstring path)
+{
+ int fd;
+ int i,err;
+ char value[PROPERTY_VALUE_MAX] = {'\0'};
+ int init_success = 0;
+ jboolean isCopy;
+ v4l2_capability cap;
+ const char* radio_path = env->GetStringUTFChars(path, &isCopy);
+
+ if(radio_path == NULL){
+ return FM_JNI_FAILURE;
+ }
+ fd = open(radio_path, O_RDONLY, O_NONBLOCK);
+ if(isCopy == JNI_TRUE){
+ env->ReleaseStringUTFChars(path, radio_path);
+ }
+ if(fd < 0){
+ return FM_JNI_FAILURE;
+ }
+ //Read the driver verions
+ err = ioctl(fd, VIDIOC_QUERYCAP, &cap);
+
+ ALOGD("VIDIOC_QUERYCAP returns :%d: version: %d \n", err , cap.version );
+
+ if (is_soc_pronto())
+ {
+ /*Set the mode for soc downloader*/
+ if (bt_configstore_intf != NULL) {
+ bt_configstore_intf->set_vendor_property(FM_PROP_HW_MODE, "normal");
+
+ /* Need to clear the hw.fm.init firstly */
+ bt_configstore_intf->set_vendor_property(FM_PROP_HW_INIT, "0");
+ bt_configstore_intf->set_vendor_property(FM_PROP_CTL_START, "fm_dl");
+
+ sched_yield();
+ for(i=0; i<45; i++) {
+ get_property(FM_PROP_HW_INIT, value);
+ if (strcmp(value, "1") == 0) {
+ init_success = 1;
+ break;
+ } else {
+ usleep(WAIT_TIMEOUT);
+ }
+ }
+ ALOGE("init_success:%d after %f seconds \n", init_success, 0.2*i);
+ if(!init_success) {
+ bt_configstore_intf->set_vendor_property(FM_PROP_CTL_STOP,"fm_dl");
+ // close the fd(power down)
+ close(fd);
+ return FM_JNI_FAILURE;
+ }
+ }
+ }
+ return fd;
+}
+
+/* native interface */
+static jint android_hardware_fmradio_FmReceiverJNI_closeFdNative
+ (JNIEnv * env, jobject thiz, jint fd)
+{
+ if (is_soc_pronto() && bt_configstore_intf != NULL)
+ {
+ bt_configstore_intf->set_vendor_property(FM_PROP_CTL_STOP,"fm_dl");
+ }
+ close(fd);
+ return FM_JNI_SUCCESS;
+}
+
+static bool is_soc_cherokee() {
+ if(strcmp(soc_name, "cherokee") == 0)
+ return true;
+ else
+ return false;
+}
+
/********************************************************************
* Current JNI
*******************************************************************/
@@ -670,13 +692,31 @@
{
int err;
long freq;
-
- err = vendor_interface->get_fm_ctrl(V4L2_CID_PRV_IRIS_FREQ, (int *)&freq);
- if (err == FM_JNI_SUCCESS) {
- err = freq;
- } else {
- err = FM_JNI_FAILURE;
- ALOGE("%s: get freq failed\n", LOG_TAG);
+ if (is_soc_cherokee())
+ {
+ err = vendor_interface->get_fm_ctrl(V4L2_CID_PRV_IRIS_FREQ, (int *)&freq);
+ if (err == FM_JNI_SUCCESS) {
+ err = freq;
+ } else {
+ err = FM_JNI_FAILURE;
+ ALOGE("%s: get freq failed\n", LOG_TAG);
+ }
+ }
+ else
+ {
+ if (fd >= 0) {
+ err = FmIoctlsInterface :: get_cur_freq(fd, freq);
+ if(err < 0) {
+ err = FM_JNI_FAILURE;
+ ALOGE("%s: get freq failed\n", LOG_TAG);
+ } else {
+ err = freq;
+ }
+ } else {
+ ALOGE("%s: get freq failed because fd is negative, fd: %d\n",
+ LOG_TAG, fd);
+ err = FM_JNI_FAILURE;
+ }
}
return err;
}
@@ -686,9 +726,26 @@
(JNIEnv * env, jobject thiz, jint fd, jint freq)
{
int err;
-
- err = vendor_interface->set_fm_ctrl(V4L2_CID_PRV_IRIS_FREQ, freq);
-
+ if (is_soc_cherokee())
+ {
+ err = vendor_interface->set_fm_ctrl(V4L2_CID_PRV_IRIS_FREQ, freq);
+ }
+ else
+ {
+ if ((fd >= 0) && (freq > 0)) {
+ err = FmIoctlsInterface :: set_freq(fd, freq);
+ if (err < 0) {
+ ALOGE("%s: set freq failed, freq: %d\n", LOG_TAG, freq);
+ err = FM_JNI_FAILURE;
+ } else {
+ err = FM_JNI_SUCCESS;
+ }
+ } else {
+ ALOGE("%s: set freq failed because either fd/freq is negative,\
+ fd: %d, freq: %d\n", LOG_TAG, fd, freq);
+ err = FM_JNI_FAILURE;
+ }
+ }
return err;
}
@@ -698,9 +755,26 @@
{
int err;
ALOGE("id(%x) value: %x\n", id, value);
-
- err = vendor_interface->set_fm_ctrl(id, value);
-
+ if (is_soc_cherokee())
+ {
+ err = vendor_interface->set_fm_ctrl(id, value);
+ }
+ else
+ {
+ if ((fd >= 0) && (id >= 0)) {
+ err = FmIoctlsInterface :: set_control(fd, id, value);
+ if (err < 0) {
+ ALOGE("%s: set control failed, id: %d\n", LOG_TAG, id);
+ err = FM_JNI_FAILURE;
+ } else {
+ err = FM_JNI_SUCCESS;
+ }
+ } else {
+ ALOGE("%s: set control failed because either fd/id is negavtive,\
+ fd: %d, id: %d\n", LOG_TAG, fd, id);
+ err = FM_JNI_FAILURE;
+ }
+ }
return err;
}
@@ -712,13 +786,33 @@
long val;
ALOGE("id(%x)\n", id);
- err = vendor_interface->get_fm_ctrl(id, (int *)&val);
- if (err < 0) {
- ALOGE("%s: get control failed, id: %d\n", LOG_TAG, id);
- err = FM_JNI_FAILURE;
- } else {
- err = val;
+ if (is_soc_cherokee())
+ {
+ err = vendor_interface->get_fm_ctrl(id, (int *)&val);
+ if (err < 0) {
+ ALOGE("%s: get control failed, id: %d\n", LOG_TAG, id);
+ err = FM_JNI_FAILURE;
+ } else {
+ err = val;
+ }
}
+ else
+ {
+ if ((fd >= 0) && (id >= 0)) {
+ err = FmIoctlsInterface :: get_control(fd, id, val);
+ if (err < 0) {
+ ALOGE("%s: get control failed, id: %d\n", LOG_TAG, id);
+ err = FM_JNI_FAILURE;
+ } else {
+ err = val;
+ }
+ } else {
+ ALOGE("%s: get control failed because either fd/id is negavtive,\
+ fd: %d, id: %d\n", LOG_TAG, fd, id);
+ err = FM_JNI_FAILURE;
+ }
+ }
+
return err;
}
@@ -727,15 +821,33 @@
(JNIEnv * env, jobject thiz, jint fd, jint dir)
{
int err;
-
- err = vendor_interface->set_fm_ctrl(V4L2_CID_PRV_IRIS_SEEK, dir);
- if (err < 0) {
- ALOGE("%s: search failed, dir: %d\n", LOG_TAG, dir);
- err = FM_JNI_FAILURE;
- } else {
- err = FM_JNI_SUCCESS;
+ if (is_soc_cherokee())
+ {
+ err = vendor_interface->set_fm_ctrl(V4L2_CID_PRV_IRIS_SEEK, dir);
+ if (err < 0) {
+ ALOGE("%s: search failed, dir: %d\n", LOG_TAG, dir);
+ err = FM_JNI_FAILURE;
+ } else {
+ err = FM_JNI_SUCCESS;
+ }
}
-
+ else
+ {
+ if ((fd >= 0) && (dir >= 0)) {
+ ALOGD("startSearchNative: Issuing the VIDIOC_S_HW_FREQ_SEEK");
+ err = FmIoctlsInterface :: start_search(fd, dir);
+ if (err < 0) {
+ ALOGE("%s: search failed, dir: %d\n", LOG_TAG, dir);
+ err = FM_JNI_FAILURE;
+ } else {
+ err = FM_JNI_SUCCESS;
+ }
+ } else {
+ ALOGE("%s: search failed because either fd/dir is negative,\
+ fd: %d, dir: %d\n", LOG_TAG, fd, dir);
+ err = FM_JNI_FAILURE;
+ }
+ }
return err;
}
@@ -745,14 +857,32 @@
{
int err;
- err = vendor_interface->set_fm_ctrl(V4L2_CID_PRV_SRCHON, 0);
- if (err < 0) {
- ALOGE("%s: cancel search failed\n", LOG_TAG);
- err = FM_JNI_FAILURE;
- } else {
- err = FM_JNI_SUCCESS;
+ if (is_soc_cherokee())
+ {
+ err = vendor_interface->set_fm_ctrl(V4L2_CID_PRV_SRCHON, 0);
+ if (err < 0) {
+ ALOGE("%s: cancel search failed\n", LOG_TAG);
+ err = FM_JNI_FAILURE;
+ } else {
+ err = FM_JNI_SUCCESS;
+ }
}
-
+ else
+ {
+ if (fd >= 0) {
+ err = FmIoctlsInterface :: set_control(fd, V4L2_CID_PRV_SRCHON, 0);
+ if (err < 0) {
+ ALOGE("%s: cancel search failed\n", LOG_TAG);
+ err = FM_JNI_FAILURE;
+ } else {
+ err = FM_JNI_SUCCESS;
+ }
+ } else {
+ ALOGE("%s: cancel search failed because fd is negative, fd: %d\n",
+ LOG_TAG, fd);
+ err = FM_JNI_FAILURE;
+ }
+ }
return err;
}
@@ -763,14 +893,32 @@
int err;
long rmssi;
- err = vendor_interface->get_fm_ctrl(V4L2_CID_PRV_IRIS_RMSSI, (int *)&rmssi);
- if (err < 0) {
- ALOGE("%s: Get Rssi failed", LOG_TAG);
- err = FM_JNI_FAILURE;
- } else {
- err = FM_JNI_SUCCESS;
+ if (is_soc_cherokee())
+ {
+ err = vendor_interface->get_fm_ctrl(V4L2_CID_PRV_IRIS_RMSSI, (int *)&rmssi);
+ if (err < 0) {
+ ALOGE("%s: Get Rssi failed", LOG_TAG);
+ err = FM_JNI_FAILURE;
+ } else {
+ err = FM_JNI_SUCCESS;
+ }
}
-
+ else
+ {
+ if (fd >= 0) {
+ err = FmIoctlsInterface :: get_rmssi(fd, rmssi);
+ if (err < 0) {
+ ALOGE("%s: get rmssi failed\n", LOG_TAG);
+ err = FM_JNI_FAILURE;
+ } else {
+ err = rmssi;
+ }
+ } else {
+ ALOGE("%s: get rmssi failed because fd is negative, fd: %d\n",
+ LOG_TAG, fd);
+ err = FM_JNI_FAILURE;
+ }
+ }
return err;
}
@@ -779,21 +927,39 @@
(JNIEnv * env, jobject thiz, jint fd, jint low, jint high)
{
int err;
-
- err = vendor_interface->set_fm_ctrl(V4L2_CID_PRV_IRIS_UPPER_BAND, high);
- if (err < 0) {
- ALOGE("%s: set band failed, high: %d\n", LOG_TAG, high);
- err = FM_JNI_FAILURE;
- return err;
+ if (is_soc_cherokee())
+ {
+ err = vendor_interface->set_fm_ctrl(V4L2_CID_PRV_IRIS_UPPER_BAND, high);
+ if (err < 0) {
+ ALOGE("%s: set band failed, high: %d\n", LOG_TAG, high);
+ err = FM_JNI_FAILURE;
+ return err;
+ }
+ err = vendor_interface->set_fm_ctrl(V4L2_CID_PRV_IRIS_LOWER_BAND, low);
+ if (err < 0) {
+ ALOGE("%s: set band failed, low: %d\n", LOG_TAG, low);
+ err = FM_JNI_FAILURE;
+ } else {
+ err = FM_JNI_SUCCESS;
+ }
}
- err = vendor_interface->set_fm_ctrl(V4L2_CID_PRV_IRIS_LOWER_BAND, low);
- if (err < 0) {
- ALOGE("%s: set band failed, low: %d\n", LOG_TAG, low);
- err = FM_JNI_FAILURE;
- } else {
- err = FM_JNI_SUCCESS;
+ else
+ {
+ if ((fd >= 0) && (low >= 0) && (high >= 0)) {
+ err = FmIoctlsInterface :: set_band(fd, low, high);
+ if (err < 0) {
+ ALOGE("%s: set band failed, low: %d, high: %d\n",
+ LOG_TAG, low, high);
+ err = FM_JNI_FAILURE;
+ } else {
+ err = FM_JNI_SUCCESS;
+ }
+ } else {
+ ALOGE("%s: set band failed because either fd/band is negative,\
+ fd: %d, low: %d, high: %d\n", LOG_TAG, fd, low, high);
+ err = FM_JNI_FAILURE;
+ }
}
-
return err;
}
@@ -803,7 +969,8 @@
{
int err;
ULINT freq;
-
+if (is_soc_cherokee())
+{
err = vendor_interface->get_fm_ctrl(V4L2_CID_PRV_IRIS_LOWER_BAND, (int *)&freq);
if (err < 0) {
ALOGE("%s: get lower band failed\n", LOG_TAG);
@@ -811,7 +978,24 @@
} else {
err = freq;
}
-
+ return err;
+}
+else
+{
+ if (fd >= 0) {
+ err = FmIoctlsInterface :: get_lowerband_limit(fd, freq);
+ if (err < 0) {
+ ALOGE("%s: get lower band failed\n", LOG_TAG);
+ err = FM_JNI_FAILURE;
+ } else {
+ err = freq;
+ }
+ } else {
+ ALOGE("%s: get lower band failed because fd is negative,\
+ fd: %d\n", LOG_TAG, fd);
+ err = FM_JNI_FAILURE;
+ }
+}
return err;
}
@@ -821,7 +1005,8 @@
{
int err;
ULINT freq;
-
+if (is_soc_cherokee())
+{
err = vendor_interface->get_fm_ctrl(V4L2_CID_PRV_IRIS_UPPER_BAND, (int *)&freq);
if (err < 0) {
ALOGE("%s: get upper band failed\n", LOG_TAG);
@@ -829,7 +1014,24 @@
} else {
err = freq;
}
-
+ return err;
+}
+else
+{
+ if (fd >= 0) {
+ err = FmIoctlsInterface :: get_upperband_limit(fd, freq);
+ if (err < 0) {
+ ALOGE("%s: get lower band failed\n", LOG_TAG);
+ err = FM_JNI_FAILURE;
+ } else {
+ err = freq;
+ }
+ } else {
+ ALOGE("%s: get lower band failed because fd is negative,\
+ fd: %d\n", LOG_TAG, fd);
+ err = FM_JNI_FAILURE;
+ }
+}
return err;
}
@@ -838,7 +1040,8 @@
{
int err;
-
+if (is_soc_cherokee())
+{
err = vendor_interface->set_fm_ctrl(V4L2_CID_PRV_IRIS_AUDIO_MODE, val);
if (err < 0) {
ALOGE("%s: set audio mode failed\n", LOG_TAG);
@@ -846,6 +1049,51 @@
} else {
err = FM_JNI_SUCCESS;
}
+ return err;
+}
+else
+{
+ if (fd >= 0) {
+ err = FmIoctlsInterface :: set_audio_mode(fd, (enum AUDIO_MODE)val);
+ if (err < 0) {
+ err = FM_JNI_FAILURE;
+ } else {
+ err = FM_JNI_SUCCESS;
+ }
+ } else {
+ err = FM_JNI_FAILURE;
+ }
+}
+ return err;
+}
+
+
+/* native interface */
+static jint android_hardware_fmradio_FmReceiverJNI_getBufferNative
+ (JNIEnv * env, jobject thiz, jint fd, jbyteArray buff, jint index)
+{
+ int err;
+ jboolean isCopy;
+ jbyte *byte_buffer = NULL;
+
+ if ((fd >= 0) && (index >= 0)) {
+ ALOGE("index: %d\n", index);
+ byte_buffer = env->GetByteArrayElements(buff, &isCopy);
+ err = FmIoctlsInterface :: get_buffer(fd,
+ (char *)byte_buffer,
+ STD_BUF_SIZE,
+ index);
+ if (err < 0) {
+ err = FM_JNI_FAILURE;
+ }
+ if (buff != NULL) {
+ ALOGE("Free the buffer\n");
+ env->ReleaseByteArrayElements(buff, byte_buffer, 0);
+ byte_buffer = NULL;
+ }
+ } else {
+ err = FM_JNI_FAILURE;
+ }
return err;
}
@@ -862,28 +1110,81 @@
static jint android_hardware_fmradio_FmReceiverJNI_configureSpurTable
(JNIEnv * env, jobject thiz, jint fd)
{
+ int err;
+
ALOGD("->android_hardware_fmradio_FmReceiverJNI_configureSpurTable\n");
- return FM_JNI_SUCCESS;
+ if (fd >= 0) {
+ err = FmIoctlsInterface :: set_control(fd,
+ V4L2_CID_PRIVATE_UPDATE_SPUR_TABLE,
+ 0);
+ if (err < 0) {
+ err = FM_JNI_FAILURE;
+ } else {
+ err = FM_JNI_SUCCESS;
+ }
+ } else {
+ err = FM_JNI_FAILURE;
+ }
+
+ return err;
}
static jint android_hardware_fmradio_FmReceiverJNI_setPSRepeatCountNative
(JNIEnv * env, jobject thiz, jint fd, jint repCount)
{
+ int masked_ps_repeat_cnt;
+ int err;
ALOGE("->android_hardware_fmradio_FmReceiverJNI_setPSRepeatCountNative\n");
- return FM_JNI_SUCCESS;
+ if (fd >= 0) {
+ masked_ps_repeat_cnt = repCount & MASK_TXREPCOUNT;
+ err = FmIoctlsInterface :: set_control(fd,
+ V4L2_CID_PRIVATE_TAVARUA_TX_SETPSREPEATCOUNT,
+ masked_ps_repeat_cnt);
+ if (err < 0) {
+ err = FM_JNI_FAILURE;
+ } else {
+ err = FM_JNI_SUCCESS;
+ }
+ } else {
+ err = FM_JNI_FAILURE;
+ }
+ return err;
}
static jint android_hardware_fmradio_FmReceiverJNI_setTxPowerLevelNative
(JNIEnv * env, jobject thiz, jint fd, jint powLevel)
{
+ int err;
ALOGE("->android_hardware_fmradio_FmReceiverJNI_setTxPowerLevelNative\n");
- return FM_JNI_SUCCESS;
+ if (fd >= 0) {
+ err = FmIoctlsInterface :: set_control(fd,
+ V4L2_CID_TUNE_POWER_LEVEL,
+ powLevel);
+ if (err < 0) {
+ err = FM_JNI_FAILURE;
+ } else {
+ err = FM_JNI_SUCCESS;
+ }
+ } else {
+ err = FM_JNI_FAILURE;
+ }
+
+ return err;
+}
+
+static void android_hardware_fmradio_FmReceiverJNI_configurePerformanceParams
+ (JNIEnv * env, jobject thiz, jint fd)
+{
+
+ ConfigFmThs thsObj;
+
+ thsObj.SetRxSearchAfThs(FM_PERFORMANCE_PARAMS, fd);
}
/* native interface */
@@ -891,7 +1192,37 @@
(JNIEnv * env, jobject thiz, jint fd, jshortArray buff, jint count)
{
ALOGE("entered JNI's setSpurDataNative\n");
+ int err, i = 0;
+ struct v4l2_ext_control ext_ctl;
+ struct v4l2_ext_controls v4l2_ctls;
+ uint8_t *data;
+ short *spur_data = env->GetShortArrayElements(buff, NULL);
+ if (spur_data == NULL) {
+ ALOGE("Spur data is NULL\n");
+ return FM_JNI_FAILURE;
+ }
+ data = (uint8_t *) malloc(count);
+ if (data == NULL) {
+ ALOGE("Allocation failed for data\n");
+ return FM_JNI_FAILURE;
+ }
+ for(i = 0; i < count; i++)
+ data[i] = (uint8_t) spur_data[i];
+ ext_ctl.id = V4L2_CID_PRIVATE_IRIS_SET_SPURTABLE;
+ ext_ctl.string = (char*)data;
+ ext_ctl.size = count;
+ v4l2_ctls.ctrl_class = V4L2_CTRL_CLASS_USER;
+ v4l2_ctls.count = 1;
+ v4l2_ctls.controls = &ext_ctl;
+
+ err = ioctl(fd, VIDIOC_S_EXT_CTRLS, &v4l2_ctls );
+ if (err < 0){
+ ALOGE("Set ioctl failed\n");
+ free(data);
+ return FM_JNI_FAILURE;
+ }
+ free(data);
return FM_JNI_SUCCESS;
}
@@ -900,8 +1231,9 @@
{
ALOGD("%s: val = %d\n", __func__, val);
int err = JNI_ERR;
+if (is_soc_cherokee()) {
err = vendor_interface->set_fm_ctrl(V4L2_CID_PRV_ENABLE_SLIMBUS, val);
-
+}
return err;
}
@@ -945,8 +1277,9 @@
{
ALOGD("%s: val = %d\n", __func__, val);
int err = JNI_ERR;
+if (is_soc_cherokee()) {
err = vendor_interface->set_fm_ctrl(V4L2_CID_PRV_SOFT_MUTE, val);
-
+}
return err;
}
@@ -1026,6 +1359,7 @@
}
static void initNative(JNIEnv *env, jobject object) {
+if (is_soc_cherokee()) {
int status;
ALOGI("Init native called \n");
@@ -1040,12 +1374,15 @@
}
mCallbacksObj = env->NewGlobalRef(object);
}
+}
static void cleanupNative(JNIEnv *env, jobject object) {
- if (mCallbacksObj != NULL) {
- env->DeleteGlobalRef(mCallbacksObj);
- mCallbacksObj = NULL;
+ if (is_soc_cherokee()) {
+ if (mCallbacksObj != NULL) {
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = NULL;
+ }
}
}
/*
@@ -1056,6 +1393,10 @@
{ "classInitNative", "()V", (void*)classInitNative},
{ "initNative", "()V", (void*)initNative},
{"cleanupNative", "()V", (void *) cleanupNative},
+ { "acquireFdNative", "(Ljava/lang/String;)I",
+ (void*)android_hardware_fmradio_FmReceiverJNI_acquireFdNative},
+ { "closeFdNative", "(I)I",
+ (void*)android_hardware_fmradio_FmReceiverJNI_closeFdNative},
{ "getFreqNative", "(I)I",
(void*)android_hardware_fmradio_FmReceiverJNI_getFreqNative},
{ "setFreqNative", "(II)I",
@@ -1076,6 +1417,8 @@
(void*)android_hardware_fmradio_FmReceiverJNI_getLowerBandNative},
{ "getUpperBandNative", "(I)I",
(void*)android_hardware_fmradio_FmReceiverJNI_getUpperBandNative},
+ { "getBufferNative", "(I[BI)I",
+ (void*)android_hardware_fmradio_FmReceiverJNI_getBufferNative},
{ "setMonoStereoNative", "(II)I",
(void*)android_hardware_fmradio_FmReceiverJNI_setMonoStereoNative},
{ "getRawRdsNative", "(I[BI)I",
@@ -1088,6 +1431,8 @@
(void*)android_hardware_fmradio_FmReceiverJNI_configureSpurTable},
{ "setSpurDataNative", "(I[SI)I",
(void*)android_hardware_fmradio_FmReceiverJNI_setSpurDataNative},
+ { "configurePerformanceParams", "(I)V",
+ (void*)android_hardware_fmradio_FmReceiverJNI_configurePerformanceParams},
{ "enableSlimbus", "(II)I",
(void*)android_hardware_fmradio_FmReceiverJNI_enableSlimbusNative},
{ "enableSoftMute", "(II)I",
diff --git a/qcom/fmradio/FmReceiver.java b/qcom/fmradio/FmReceiver.java
index 48e4d11..b7c1613 100644
--- a/qcom/fmradio/FmReceiver.java
+++ b/qcom/fmradio/FmReceiver.java
@@ -366,6 +366,14 @@
return false;
}
+ public static boolean isCherokeeChip() {
+ String chip = FmReceiverJNI.getSocNameNative();
+ if (chip.equals("cherokee"))
+ return true;
+ else
+ return false;
+ }
+
public PhoneStateListener mDataConnectionStateListener = new PhoneStateListener(){
public void onDataConnectionStateChanged(int state, int networkType) {
Log.d (TAG, "state: " + Integer.toString(state) + " networkType: " + Integer.toString(networkType));
@@ -445,6 +453,7 @@
public FmReceiver(){
mControl = new FmRxControls();
mRdsData = new FmRxRdsData (sFd);
+ mRxEvents = new FmRxEventListner();
}
/**
@@ -459,11 +468,79 @@
public FmReceiver(String devicePath,
FmRxEvCallbacksAdaptor callback) throws InstantiationException {
mControl = new FmRxControls();
+ mRxEvents = new FmRxEventListner();
Log.e(TAG, "FmReceiver constructor");
//registerClient(callback);
mCallback = callback;
- mFmReceiverJNI = new FmReceiverJNI(mCallback);
+ if (isCherokeeChip()) {
+ mFmReceiverJNI = new FmReceiverJNI(mCallback);
+ }
+ }
+
+
+ /*==============================================================
+ FUNCTION: registerClient
+ ==============================================================*/
+ /**
+ * Registers a callback for FM receiver event
+ * notifications.
+ * <p>
+ * This is a synchronous command used to register for event
+ * notifications from the FM receiver driver. Since the FM
+ * driver performs some tasks asynchronously, this function
+ * allows the client to receive information asynchronously.
+ * <p>
+ * When calling this function, the client must pass a callback
+ * function which will be used to deliver asynchronous events.
+ * The argument callback must be a non-NULL value. If a NULL
+ * value is passed to this function, the registration will
+ * fail.
+ * <p>
+ * The client can choose which events will be sent from the
+ * receiver driver by simply implementing functions for events
+ * it wishes to receive.
+ * <p>
+ * @param callback the callbacks to handle the events
+ * events from the FM receiver.
+ * @return true if Callback registered, false if Callback
+ * registration failed.
+ * <p>
+ * @see #acquire
+ * @see #unregisterClient
+ *
+ */
+ public boolean registerClient(FmRxEvCallbacks callback){
+ boolean status;
+ status = super.registerClient(callback);
+ /* Do Receiver Specific Stuff here.*/
+
+ return status;
+ }
+
+ /*==============================================================
+ FUNCTION: unregisterClient
+ ==============================================================*/
+ /**
+ * UnRegisters a client's event notification callback.
+ *
+ * This is a synchronous command used to unregister a client's
+ * event callback.
+ * <p>
+ * @return true Always returns true.
+ * <p>
+ * @see #acquire
+ * @see #release
+ * @see #registerClient
+ *
+ */
+ public boolean unregisterClient () {
+ boolean status;
+
+ status = super.unregisterClient();
+
+ /* Do Receiver Specific Stuff here.*/
+ return status;
}
/*==============================================================
@@ -531,6 +608,10 @@
status = super.enable(configSettings, FmTransceiver.FM_RX);
if (status == true ) {
+ if (!isCherokeeChip()) {
+ /* Do Receiver Specific Enable Stuff here.*/
+ status = registerClient(mCallback);
+ }
mRdsData = new FmRxRdsData(sFd);
registerDataConnectionStateListener(app_context);
app_context.registerReceiver(mReceiver, mIntentFilter);
@@ -591,6 +672,10 @@
setFMPowerState(FMState_Turned_Off);
Log.v(TAG, "reset: NEW-STATE : FMState_Turned_Off");
+ status = unregisterClient();
+
+ release("/dev/radio0");
+
return status;
}
@@ -1491,7 +1576,11 @@
int piLower = 0;
int piHigher = 0;
- buff = FmReceiverJNI.getPsBuffer(buff);
+ if(isCherokeeChip()) {
+ buff = FmReceiverJNI.getPsBuffer(buff);
+ }
+ else
+ FmReceiverJNI.getBufferNative(sFd, buff, 3);
/* byte is signed ;(
* knock down signed bits
@@ -1544,8 +1633,12 @@
int piLower = 0;
int piHigher = 0;
- buff = FmReceiverJNI.getPsBuffer(buff);
-
+ if (isCherokeeChip()) {
+ buff = FmReceiverJNI.getPsBuffer(buff);
+ }
+ else {
+ FmReceiverJNI.getBufferNative(sFd, buff, 2);
+ }
String rdsStr = new String(buff);
/* byte is signed ;(
* knock down signed bit
@@ -1574,8 +1667,13 @@
int rt_len;
int i, j = 2;
byte tag_code, tag_len, tag_start_pos;
- rt_plus = FmReceiverJNI.getPsBuffer(rt_plus);
-
+ if (isCherokeeChip()) {
+ rt_plus = FmReceiverJNI.getPsBuffer(rt_plus);
+ }
+ else
+ {
+ bytes_read = FmReceiverJNI.getBufferNative(sFd, rt_plus, BUF_RTPLUS);
+ }
bytes_read = rt_plus[0];
if (bytes_read > 0) {
if (rt_plus[RT_OR_ERT_IND] == 0)
@@ -1612,8 +1710,14 @@
String encoding_type = "UCS-2";
int bytes_read;
- raw_ert = FmReceiverJNI.getPsBuffer(raw_ert);
-
+ if(isCherokeeChip())
+ {
+ raw_ert = FmReceiverJNI.getPsBuffer(raw_ert);
+ }
+ else
+ {
+ bytes_read = FmReceiverJNI.getBufferNative(sFd, raw_ert, BUF_ERT);
+ }
bytes_read = raw_ert[0];
if (bytes_read > 0) {
ert_text = new byte[raw_ert[LEN_IND]];
@@ -1680,29 +1784,58 @@
int [] AfList = new int [50];
int lowerBand, i;
int tunedFreq, PI, size_AFLIST;
+ if (isCherokeeChip()) {
+ buff = FmReceiverJNI.getPsBuffer(buff);
+ }
+ else
+ {
+ FmReceiverJNI.getBufferNative(sFd, buff, TAVARUA_BUF_AF_LIST);
+ }
+ if (isSmdTransportLayer() || isRomeChip() || isCherokeeChip()) {
+ Log.d(TAG, "SMD transport layer or Rome chip");
- buff = FmReceiverJNI.getPsBuffer(buff);
+ tunedFreq = (buff[0] & 0xFF) |
+ ((buff[1] & 0xFF) << 8) |
+ ((buff[2] & 0xFF) << 16) |
+ ((buff[3] & 0xFF) << 24) ;
+ Log.d(TAG, "tunedFreq = " +tunedFreq);
- tunedFreq = (buff[0] & 0xFF) |
- ((buff[1] & 0xFF) << 8) |
- ((buff[2] & 0xFF) << 16) |
- ((buff[3] & 0xFF) << 24) ;
- Log.d(TAG, "tunedFreq = " +tunedFreq);
- PI = (buff[4] & 0xFF) |
- ((buff[5] & 0xFF) << 8);
- Log.d(TAG, "PI: " + PI);
- size_AFLIST = buff[6] & 0xFF;
- Log.d(TAG, "size_AFLIST : " +size_AFLIST);
+ PI = (buff[4] & 0xFF) |
+ ((buff[5] & 0xFF) << 8);
+ Log.d(TAG, "PI: " + PI);
- for (i = 0;i < size_AFLIST;i++) {
- AfList[i] = (buff[6 + i * 4 + 1] & 0xFF) |
- ((buff[6 + i * 4 + 2] & 0xFF) << 8) |
- ((buff[6 + i * 4 + 3] & 0xFF) << 16) |
- ((buff[6 + i * 4 + 4] & 0xFF) << 24) ;
- Log.d(TAG, "AF: " + AfList[i]);
+ size_AFLIST = buff[6] & 0xFF;
+ Log.d(TAG, "size_AFLIST : " +size_AFLIST);
+
+ for (i = 0;i < size_AFLIST;i++) {
+ AfList[i] = (buff[6 + i * 4 + 1] & 0xFF) |
+ ((buff[6 + i * 4 + 2] & 0xFF) << 8) |
+ ((buff[6 + i * 4 + 3] & 0xFF) << 16) |
+ ((buff[6 + i * 4 + 4] & 0xFF) << 24) ;
+ Log.d(TAG, "AF: " + AfList[i]);
+ }
+ } else {
+
+ if ((buff[4] <= 0) || (buff[4] > 25))
+ return null;
+
+ lowerBand = FmReceiverJNI.getLowerBandNative(sFd);
+ Log.d (TAG, "Low band " + lowerBand);
+
+ Log.d (TAG, "AF_buff 0: " + (buff[0] & 0xff));
+ Log.d (TAG, "AF_buff 1: " + (buff[1] & 0xff));
+ Log.d (TAG, "AF_buff 2: " + (buff[2] & 0xff));
+ Log.d (TAG, "AF_buff 3: " + (buff[3] & 0xff));
+ Log.d (TAG, "AF_buff 4: " + (buff[4] & 0xff));
+
+ for (i=0; i<buff[4]; i++) {
+ AfList[i] = ((buff[i+4] & 0xFF) * 1000) + lowerBand;
+ Log.d (TAG, "AF : " + AfList[i]);
+ }
}
return AfList;
+
}
/*==============================================================
@@ -2271,6 +2404,39 @@
}
/*==============================================================
+ FUNCTION: getStationList
+ ==============================================================*/
+ /**
+ * Returns a frequency List of the searched stations.
+ *
+ * <p>
+ * This method retreives the results of the {@link
+ * #searchStationList}. This method should be called when the
+ * FmRxEvSearchListComplete is invoked.
+ *
+ * <p>
+ * @return An array of integers that corresponds to the
+ * frequency of the searched Stations
+ * @see #searchStationList
+ */
+ public int[] getStationList ()
+ {
+ int state = getFMState();
+ /* Check current state of FM device */
+ if (state == FMState_Turned_Off || state == FMState_Srch_InProg) {
+ Log.d(TAG, "getStationList: Device currently busy in executing another command.");
+ return null;
+ }
+ int[] stnList = new int [100];
+
+ stnList = mControl.stationList (sFd);
+
+ return stnList;
+
+ }
+
+
+ /*==============================================================
FUNCTION: getRssi
==============================================================*/
/**
@@ -2763,6 +2929,38 @@
return retval;
}
+ public static void getSpurTableData()
+ {
+ int freq;
+ byte no_of_spurs;
+ int rotation_value;
+ byte lsbOfLen;
+ byte filterCoe;
+ byte isEnbale;
+ byte [] buff = new byte[STD_BUF_SIZE];
+ int i = 0;
+ FmReceiverJNI.getBufferNative(sFd, buff, 13);
+
+ freq = buff[0] & 0xFF;
+ freq |= ((buff[1] & 0xFF) << 8);
+ freq |= ((buff[2] & 0xFF) << 16);
+ Log.d (TAG, "freq = " +freq);
+ no_of_spurs = buff[3];
+ Log.d (TAG, "no_of_spurs = " + no_of_spurs);
+ for(i = 0; i < FmConfig.no_Of_Spurs_For_Entry; i++) {
+ rotation_value = buff[(i * 4) + 4] & 0xFF;
+ rotation_value |= ((buff[(i * 4) + 5] & 0xFF) << 8);
+ rotation_value |= ((buff[(i * 4) + 6] & 0x0F) << 12);
+ Log.d (TAG, "rotation_value = " +rotation_value);
+ lsbOfLen = (byte) (((buff[(i * 4) + 6] & 0xF0) >> 4) & 0x01);
+ Log.d (TAG, "lsbOfLen = "+lsbOfLen);
+ filterCoe = (byte) (((buff[(i * 4) + 6] & 0xF0) >> 5) & 0x03);
+ Log.d (TAG, "filterCoe = " +filterCoe);
+ isEnbale = (byte) (((buff[(i * 4) + 6] & 0xF0) >> 7) & 0x01);
+ Log.d (TAG, "spur level: " +buff[(i * 4) + 7]);
+ }
+ return;
+ }
public void FMcontrolLowPassFilter(int state, int net_type, int enable) {
int RatConf = getFmWanWlanCoexProp(WAN_RATCONF);
Log.v (TAG, "FMcontrolLowPassFilter " + RatConf);
diff --git a/qcom/fmradio/FmReceiverJNI.java b/qcom/fmradio/FmReceiverJNI.java
index 298ed07..86a7df7 100644
--- a/qcom/fmradio/FmReceiverJNI.java
+++ b/qcom/fmradio/FmReceiverJNI.java
@@ -319,6 +319,8 @@
Log.d(TAG, "FmReceiverJNI constructor called");
}
+ static native int acquireFdNative(String path);
+
/**
* native method:
* @param fd
@@ -338,6 +340,15 @@
static native int cancelSearchNative(int fd);
/**
+ * native method: release control of device
+ * @param fd file descriptor of device
+ * @return May return
+ * {@link #FM_JNI_SUCCESS}
+ * {@link #FM_JNI_FAILURE}
+ */
+ static native int closeFdNative(int fd);
+
+ /**
* native method: get frequency
* @param fd file descriptor of device
* @return Returns frequency in int form
@@ -383,6 +394,16 @@
static native int startSearchNative (int fd, int dir);
/**
+ * native method: get buffer
+ * @param fd file descriptor of device
+ * @param buff[] buffer
+ * @param index index of the buffer to be retrieved
+ * @return {@link #FM_JNI_SUCCESS}
+ * {@link #FM_JNI_FAILURE}
+ */
+ static native int getBufferNative (int fd, byte buff[], int index);
+
+ /**
* native method: get RSSI value of the
* received signal
* @param fd file descriptor of device
@@ -468,6 +489,7 @@
* {@link #FM_JNI_FAILURE}
*/
static native int setSpurDataNative(int fd, short buff[], int len);
+ static native void configurePerformanceParams(int fd);
static native int enableSlimbus(int fd, int val);
static native int enableSoftMute(int fd, int val);
static native String getSocNameNative();
diff --git a/qcom/fmradio/FmRxControls.java b/qcom/fmradio/FmRxControls.java
index 1891ece..c67b7e4 100644
--- a/qcom/fmradio/FmRxControls.java
+++ b/qcom/fmradio/FmRxControls.java
@@ -123,11 +123,12 @@
Log.d(TAG,"setControlNative faile" + V4L2_CID_PRIVATE_TAVARUA_STATE);
return re;
}
- boolean ret = enableSoftMute(fd,ENABLE_SOFT_MUTE);
- if(false == ret) {
- Log.d(TAG,"enableSoftMute failed");
+ if (FmReceiver.isCherokeeChip()) {
+ boolean ret = enableSoftMute(fd,ENABLE_SOFT_MUTE);
+ if(false == ret) {
+ Log.d(TAG,"enableSoftMute failed");
+ }
}
-
setAudioPath(fd, false);
return re;
}
@@ -438,6 +439,69 @@
}
+ /* Read search list from buffer */
+ public int[] stationList (int fd)
+ {
+ int freq = 0;
+ int i=0, j = 0;
+ int station_num = 0;
+ float real_freq = 0;
+ int [] stationList;
+ byte [] sList = new byte[100];
+ int tmpFreqByte1=0;
+ int tmpFreqByte2=0;
+ float lowBand, highBand;
+
+
+ lowBand = (float) (FmReceiverJNI.getLowerBandNative(fd) / 1000.00);
+ highBand = (float) (FmReceiverJNI.getUpperBandNative(fd) / 1000.00);
+
+ Log.d(TAG, "lowBand: " + lowBand);
+ Log.d(TAG, "highBand: " + highBand);
+
+ FmReceiverJNI.getBufferNative(fd, sList, 0);
+
+ if ((int)sList[0] >0) {
+ station_num = (int)sList[0];
+ }
+ stationList = new int[station_num+1];
+ Log.d(TAG, "station_num: " + station_num);
+
+ for (i=0;i<station_num;i++) {
+ freq = 0;
+ Log.d(TAG, " Byte1 = " + sList[i*2+1]);
+ Log.d(TAG, " Byte2 = " + sList[i*2+2]);
+ tmpFreqByte1 = sList[i*2+1] & 0xFF;
+ tmpFreqByte2 = sList[i*2+2] & 0xFF;
+ Log.d(TAG, " tmpFreqByte1 = " + tmpFreqByte1);
+ Log.d(TAG, " tmpFreqByte2 = " + tmpFreqByte2);
+ freq = (tmpFreqByte1 & 0x03) << 8;
+ freq |= tmpFreqByte2;
+ Log.d(TAG, " freq: " + freq);
+ real_freq = (float)(freq * 50) + (lowBand * FREQ_MUL);//tuner.rangelow * FREQ_MUL;
+ Log.d(TAG, " real_freq: " + real_freq);
+ if ( (real_freq < (lowBand * FREQ_MUL)) || (real_freq > (highBand * FREQ_MUL)) ) {
+ Log.e(TAG, "Frequency out of band limits");
+ }
+ else {
+ stationList[j] = (int)(real_freq);
+ Log.d(TAG, " stationList: " + stationList[j]);
+ j++;
+ }
+ }
+
+ try {
+ // mark end of list
+ stationList[station_num] = 0;
+ }
+ catch (ArrayIndexOutOfBoundsException e) {
+ Log.d(TAG, "ArrayIndexOutOfBoundsException !!");
+ }
+
+ return stationList;
+
+ }
+
/* configure various search parameters and start search */
public int searchStations (int fd, int mode, int dwell,
diff --git a/qcom/fmradio/FmRxEventListner.java b/qcom/fmradio/FmRxEventListner.java
new file mode 100644
index 0000000..c178f17
--- /dev/null
+++ b/qcom/fmradio/FmRxEventListner.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2009,2012-2014, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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.
+ */
+
+package qcom.fmradio;
+import qcom.fmradio.FmReceiver;
+import qcom.fmradio.FmTransceiver;
+import java.util.Arrays;
+import android.util.Log;
+
+
+class FmRxEventListner {
+
+ private final int EVENT_LISTEN = 1;
+
+ private final int STD_BUF_SIZE = 256;
+
+ private enum FmRxEvents {
+ 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,
+ TAVARUA_EVT_NEW_SRCH_LIST,
+ TAVARUA_EVT_NEW_AF_LIST
+ }
+
+ private Thread mThread;
+ private static final String TAG = "FMRadio";
+
+ public void startListner (final int fd, final FmRxEvCallbacks cb) {
+ /* start a thread and listen for messages */
+ mThread = new Thread(){
+ public void run(){
+ byte [] buff = new byte[STD_BUF_SIZE];
+ Log.d(TAG, "Starting listener " + fd);
+
+ while ((!Thread.currentThread().isInterrupted())) {
+
+ try {
+ int index = 0;
+ int state = 0;
+ Arrays.fill(buff, (byte)0x00);
+ int freq = 0;
+ int eventCount = FmReceiverJNI.getBufferNative (fd, buff, EVENT_LISTEN);
+
+ if (eventCount >= 0)
+ Log.d(TAG, "Received event. Count: " + eventCount);
+
+ for ( index = 0; index < eventCount; index++ ) {
+ Log.d(TAG, "Received <" +buff[index]+ ">" );
+
+ switch(buff[index]){
+ case 0:
+ Log.d(TAG, "Got READY_EVENT");
+ if(FmTransceiver.getFMPowerState() == FmTransceiver.subPwrLevel_FMRx_Starting) {
+ /*Set the state as FMRxOn */
+ FmTransceiver.setFMPowerState(FmTransceiver.FMState_Rx_Turned_On);
+ Log.v(TAG, "RxEvtList: CURRENT-STATE : FMRxStarting ---> NEW-STATE : FMRxOn");
+ cb.FmRxEvEnableReceiver();
+ FmReceiverJNI.configurePerformanceParams(fd);
+ }
+ else if (FmTransceiver.getFMPowerState() == FmTransceiver.subPwrLevel_FMTurning_Off) {
+ /*Set the state as FMOff */
+ FmTransceiver.setFMPowerState(FmTransceiver.FMState_Turned_Off);
+ Log.v(TAG, "RxEvtList: CURRENT-STATE : FMTurningOff ---> NEW-STATE : FMOff");
+ FmTransceiver.release("/dev/radio0");
+ cb.FmRxEvDisableReceiver();
+ Thread.currentThread().interrupt();
+ }
+ break;
+ case 1:
+ Log.d(TAG, "Got TUNE_EVENT");
+ freq = FmReceiverJNI.getFreqNative(fd);
+ state = FmReceiver.getSearchState();
+ switch(state) {
+ case FmTransceiver.subSrchLevel_SrchAbort:
+ Log.v(TAG, "Current state is SRCH_ABORTED");
+ Log.v(TAG, "Aborting on-going search command...");
+ /* intentional fall through */
+ case FmTransceiver.subSrchLevel_SeekInPrg :
+ Log.v(TAG, "Current state is " + state);
+ FmReceiver.setSearchState(FmTransceiver.subSrchLevel_SrchComplete);
+ Log.v(TAG, "RxEvtList: CURRENT-STATE : Search ---> NEW-STATE : FMRxOn");
+ cb.FmRxEvSearchComplete(freq);
+ break;
+ default:
+ if (freq > 0)
+ cb.FmRxEvRadioTuneStatus(freq);
+ else
+ Log.e(TAG, "get frequency command failed");
+ break;
+ }
+ break;
+ case 2:
+ Log.d(TAG, "Got SEEK_COMPLETE_EVENT");
+ state = FmReceiver.getSearchState();
+ switch(state) {
+ case FmTransceiver.subSrchLevel_ScanInProg:
+ Log.v(TAG, "Current state is " + state);
+ FmReceiver.setSearchState(FmTransceiver.subSrchLevel_SrchComplete);
+ Log.v(TAG, "RxEvtList: CURRENT-STATE : Search ---> NEW-STATE :FMRxOn");
+ cb.FmRxEvSearchComplete(FmReceiverJNI.getFreqNative(fd));
+ break;
+ case FmTransceiver.subSrchLevel_SrchAbort:
+ Log.v(TAG, "Current state is SRCH_ABORTED");
+ Log.v(TAG, "Aborting on-going search command...");
+ FmReceiver.setSearchState(FmTransceiver.subSrchLevel_SrchComplete);
+ Log.v(TAG, "RxEvtList: CURRENT-STATE : Search ---> NEW-STATE : FMRxOn");
+ cb.FmRxEvSearchComplete(FmReceiverJNI.getFreqNative(fd));
+ break;
+ }
+ break;
+ case 3:
+ Log.d(TAG, "Got SCAN_NEXT_EVENT");
+ cb.FmRxEvSearchInProgress();
+ break;
+ case 4:
+ Log.d(TAG, "Got RAW_RDS_EVENT");
+ cb.FmRxEvRdsGroupData();
+ break;
+ case 5:
+ Log.d(TAG, "Got RT_EVENT");
+ cb.FmRxEvRdsRtInfo();
+ break;
+ case 6:
+ Log.d(TAG, "Got PS_EVENT");
+ cb.FmRxEvRdsPsInfo();
+ break;
+ case 7:
+ Log.d(TAG, "Got ERROR_EVENT");
+ break;
+ case 8:
+ Log.d(TAG, "Got BELOW_TH_EVENT");
+ cb.FmRxEvServiceAvailable (false);
+ break;
+ case 9:
+ Log.d(TAG, "Got ABOVE_TH_EVENT");
+ cb.FmRxEvServiceAvailable(true);
+ break;
+ case 10:
+ Log.d(TAG, "Got STEREO_EVENT");
+ cb.FmRxEvStereoStatus (true);
+ break;
+ case 11:
+ Log.d(TAG, "Got MONO_EVENT");
+ cb.FmRxEvStereoStatus (false);
+ break;
+ case 12:
+ Log.d(TAG, "Got RDS_AVAL_EVENT");
+ cb.FmRxEvRdsLockStatus (true);
+ break;
+ case 13:
+ Log.d(TAG, "Got RDS_NOT_AVAL_EVENT");
+ cb.FmRxEvRdsLockStatus (false);
+ break;
+ case 14:
+ Log.d(TAG, "Got NEW_SRCH_LIST");
+ state = FmReceiver.getSearchState();
+ switch(state) {
+ case FmTransceiver.subSrchLevel_SrchListInProg:
+ Log.v(TAG, "FmRxEventListener: Current state is AUTO_PRESET_INPROGRESS");
+ FmReceiver.setSearchState(FmTransceiver.subSrchLevel_SrchComplete);
+ Log.v(TAG, "RxEvtList: CURRENT-STATE : Search ---> NEW-STATE : FMRxOn");
+ cb.FmRxEvSearchListComplete ();
+ break;
+ case FmTransceiver.subSrchLevel_SrchAbort:
+ Log.v(TAG, "Current state is SRCH_ABORTED");
+ Log.v(TAG, "Aborting on-going SearchList command...");
+ FmReceiver.setSearchState(FmTransceiver.subSrchLevel_SrchComplete);
+ Log.v(TAG, "RxEvtList: CURRENT-STATE : Search ---> NEW-STATE : FMRxOn");
+ cb.FmRxEvSearchCancelled();
+ break;
+ }
+ break;
+ case 15:
+ Log.d(TAG, "Got NEW_AF_LIST");
+ cb.FmRxEvRdsAfInfo();
+ break;
+ case 18:
+ Log.d(TAG, "Got RADIO_DISABLED");
+ if (FmTransceiver.getFMPowerState() == FmTransceiver.subPwrLevel_FMTurning_Off) {
+ FmTransceiver.release("/dev/radio0");
+ /*Set the state as FMOff */
+ FmTransceiver.setFMPowerState(FmTransceiver.FMState_Turned_Off);
+ cb.FmRxEvDisableReceiver();
+ Log.v(TAG, "RxEvtList: CURRENT-STATE : FMTurningOff ---> NEW-STATE : FMOff");
+ Thread.currentThread().interrupt();
+ } else {
+ Log.d(TAG, "Unexpected RADIO_DISABLED recvd");
+ FmTransceiver.release("/dev/radio0");
+ cb.FmRxEvRadioReset();
+ FmTransceiver.setFMPowerState(FmTransceiver.FMState_Turned_Off);
+ Log.v(TAG, "RxEvtList: CURRENT-STATE : FMRxOn ---> NEW-STATE : FMOff");
+ Thread.currentThread().interrupt();
+ }
+ break;
+ case 19:
+ FmTransceiver.setRDSGrpMask(0);
+ break;
+ case 20:
+ Log.d(TAG, "got RT plus event");
+ cb.FmRxEvRTPlus();
+ break;
+ case 21:
+ Log.d(TAG, "got eRT event");
+ cb.FmRxEvERTInfo();
+ break;
+ case 22:
+ Log.d(TAG, "got IRIS_EVT_SPUR_TBL event");
+ FmReceiver.getSpurTableData();
+ break;
+ default:
+ Log.d(TAG, "Unknown event");
+ break;
+ }
+ }//end of for
+ } catch ( Exception ex ) {
+ Log.d( TAG, "RunningThread InterruptedException");
+ ex.printStackTrace();
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ };
+ mThread.start();
+ }
+
+ public void stopListener(){
+ //mThread.stop();
+ //Thread stop is deprecate API
+ //Interrupt the thread and check for the thread status
+ // and return from the run() method to stop the thread
+ //properly
+ Log.d( TAG, "stopping the Listener\n");
+ if( mThread != null ) {
+ mThread.interrupt();
+ }
+ }
+
+}
diff --git a/qcom/fmradio/FmTransceiver.java b/qcom/fmradio/FmTransceiver.java
index eee89d4..589a2dc 100644
--- a/qcom/fmradio/FmTransceiver.java
+++ b/qcom/fmradio/FmTransceiver.java
@@ -139,14 +139,168 @@
private static final int V4L2_CID_PRIVATE_TAVARUA_SET_NOTCH_FILTER = V4L2_CID_PRIVATE_BASE + 40;
private final String TAG = "FmTransceiver";
+ private final String V4L2_DEVICE = "/dev/radio0";
protected static int sFd;
protected FmRxControls mControl;
protected int mPowerMode;
+ protected FmRxEventListner mRxEvents;
protected FmRxRdsData mRdsData;
+
public static final int ERROR = -1;
/*==============================================================
+ FUNCTION: acquire
+ ==============================================================*/
+ /**
+ * Allows access to the V4L2 FM device.
+ *
+ * This synchronous call allows a client to use the V4L2 FM
+ * device. This must be the first call issued by the client
+ * before any receiver interfaces can be used.
+ *
+ * This call also powers up the FM Module.
+ *
+ * @param device String that is path to radio device
+ *
+ * @return true if V4L2 FM device acquired, false if V4L2 FM
+ * device could not be acquired, possibly acquired by
+ * other client
+ * @see #release
+ *
+ */
+ protected boolean acquire(String device){
+ boolean bStatus = true;
+ if (sFd <= 0) { // if previous open fails fd will be -ve.
+ sFd = FmReceiverJNI.acquireFdNative(V4L2_DEVICE);
+
+ if (sFd > 0) {
+ Log.d(TAG, "Opened "+ sFd);
+ bStatus = true;
+ }
+ else {
+ Log.d(TAG, "Fail to Open "+ sFd);
+ bStatus = false;
+ }
+ }
+ else {
+ Log.d(TAG, "Already Opened:" + sFd);
+ /*This should be case
+ * Where User try to opne the device
+ * secondtime.
+ * Case where Tx and Rx try to
+ * acquire the device
+ */
+ bStatus = false;
+ }
+ return (bStatus);
+ }
+
+ /*==============================================================
+ FUNCTION: release
+ ==============================================================*/
+ /**
+ * Releases access to the V4L2 FM device.
+ * <p>
+ * This synchronous call allows a client to release control of
+ * V4L2 FM device. This function should be called when the FM
+ * device is no longer needed. This should be the last call
+ * issued by the FM client. Once called, the client must call
+ * #acquire to re-aquire the V4L2 device control before the
+ * FM device can be used again.
+ * <p>
+ * Before the client can release control of the FM receiver
+ * interface, it must disable the FM receiver, if the client
+ * enabled it, and unregister any registered callback. If the
+ * client has ownership of the receiver, it will automatically
+ * be returned to the system.
+ * <p>
+ * This call also powers down the FM Module.
+ * <p>
+ * @param device String that is path to radio device
+ * @return true if V4L2 FM device released, false if V4L2 FM
+ * device could not be released
+ * @see #acquire
+ */
+ static boolean release(String device) {
+ if (sFd!=0)
+ {
+ FmReceiverJNI.closeFdNative(sFd);
+ sFd = 0;
+ Log.d("FmTransceiver", "Turned off: " + sFd);
+ } else
+ {
+ Log.d("FmTransceiver", "Error turning off");
+ }
+ return true;
+ }
+
+ /*==============================================================
+ FUNCTION: registerClient
+ ==============================================================*/
+ /**
+ * Registers a callback for FM receiver event notifications.
+ * <p>
+ * This is a synchronous call used to register for event
+ * notifications from the FM receiver driver. Since the FM
+ * driver performs some tasks asynchronously, this function
+ * allows the client to receive information asynchronously.
+ * <p>
+ * When calling this function, the client must pass a callback
+ * function which will be used to deliver asynchronous events.
+ * The argument callback must be a non-NULL value. If a NULL
+ * value is passed to this function, the registration will
+ * fail.
+ * <p>
+ * The client can choose which events will be sent from the
+ * receiver driver by simply implementing functions for events
+ * it wishes to receive.
+ * <p>
+ *
+ * @param callback the callback to handle the events events
+ * from the FM receiver.
+ * @return true if Callback registered, false if Callback
+ * registration failed.
+ *
+ * @see #acquire
+ * @see #unregisterClient
+ *
+ */
+ public boolean registerClient(FmRxEvCallbacks callback){
+ boolean bReturnStatus = false;
+ if (callback!=null)
+ {
+ mRxEvents.startListner(sFd, callback);
+ bReturnStatus = true;
+ } else
+ {
+ Log.d(TAG, "Null, do nothing");
+ }
+ return bReturnStatus;
+ }
+
+ /*==============================================================
+ FUNCTION: unregisterClient
+ ==============================================================*/
+ /**
+ * Unregisters a client's event notification callback.
+ * <p>
+ * This is a synchronous call used to unregister a client's
+ * event callback.
+ * <p>
+ * @return true always.
+ *
+ * @see #acquire
+ * @see #release
+ * @see #registerClient
+ *
+ */
+ public boolean unregisterClient () {
+ mRxEvents.stopListener();
+ return true;
+ }
+
+ /*==============================================================
FUNCTION: enable
==============================================================*/
/**
@@ -183,6 +337,12 @@
boolean status;
int ret;
+ if (!FmReceiver.isCherokeeChip()) {
+ //Acquire the deviceon Enable
+ if (!acquire("/dev/radio0")) {
+ return false;
+ }
+ }
if (new File("/etc/fm/SpurTableFile.txt").isFile()) {
Log.d(TAG, "Send Spur roation table");
FmConfig.fmSpurConfig(sFd);
@@ -193,6 +353,7 @@
ret = mControl.fmOn(sFd, device);
if (ret < 0) {
Log.d(TAG, "turning on failed");
+ FmReceiverJNI.closeFdNative(sFd);
sFd = 0;
return false;
}
@@ -201,6 +362,7 @@
status = FmConfig.fmConfigure (sFd, configSettings);
if (!status) {
Log.d(TAG, "fmConfigure failed");
+ FmReceiverJNI.closeFdNative(sFd);
sFd = 0;
}
return status;