Add FM Serial interface to allow FM HAL send/receive cmds/events
FM Serial interface provides APIs for FM HAL layer to send cmds
and receive events/data to and from the Controller.
Change-Id: Idbe21e536a6039387c7411b8a9322417e4ec6129
diff --git a/fm_hci/Android.mk b/fm_hci/Android.mk
new file mode 100644
index 0000000..1c4b949
--- /dev/null
+++ b/fm_hci/Android.mk
@@ -0,0 +1,37 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Setup bdroid local make variables for handling configuration
+ifneq ($(BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR),)
+ bdroid_C_INCLUDES := $(BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR)
+ bdroid_CFLAGS += -DHAS_BDROID_BUILDCFG
+else
+ bdroid_C_INCLUDES :=
+ bdroid_CFLAGS += -DHAS_NO_BDROID_BUILDCFG
+endif
+
+BDROID_DIR:= external/bluetooth/bluedroid
+
+LOCAL_CFLAGS += $(bdroid_CFLAGS)
+
+LOCAL_SRC_FILES := \
+ fm_hci.c
+
+LOCAL_SHARED_LIBRARIES := \
+ libdl \
+ libcutils
+
+LOCAL_CFLAGS := -Wno-unused-parameter
+
+LOCAL_CFLAGS += -std=c99
+
+LOCAL_C_INCLUDES += \
+ $(BDROID_DIR)/hci/include \
+ $(LOCAL_PATH)/../helium
+
+LOCAL_MODULE := libfm-hci
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/fm_hci/fm_hci.c b/fm_hci/fm_hci.c
new file mode 100644
index 0000000..2c3e2df
--- /dev/null
+++ b/fm_hci/fm_hci.c
@@ -0,0 +1,508 @@
+/*
+ * 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 "hci.h"
+#include "userial.h"
+#include "utils.h"
+#include "fm_hci.h"
+#include "wcnss_hci.h"
+#include <dlfcn.h>
+#include <sys/eventfd.h>
+#include <errno.h>
+
+int fd;
+fm_hal_cb *hal_cb;
+
+void event_notification(uint16_t event)
+{
+ pthread_mutex_lock(&fmHCIControlBlock.event_lock);
+ ready_events |= event;
+ pthread_cond_signal(&fmHCIControlBlock.event_cond);
+ ALOGI("%s: Notifying worker thread with event: %d", __func__, event);
+ pthread_mutex_unlock(&fmHCIControlBlock.event_lock);
+}
+
+bt_vendor_interface_t *fm_vnd_if = NULL;
+void init_vnd_if()
+{
+ void *dlhandle;
+ 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 !!!");
+ return;
+ }
+
+ fm_vnd_if = (bt_vendor_interface_t *) dlsym(dlhandle, "BLUETOOTH_VENDOR_LIB_INTERFACE");
+ if (!fm_vnd_if)
+ {
+ ALOGE("!!! Failed to get bt vendor interface !!!");
+ return;
+ }
+
+ ALOGI("FM-HCI: Registering the WCNSS HAL library by passing CBs and BD addr.");
+ fm_vnd_if->init(&fm_vendor_callbacks, bdaddr);
+}
+
+volatile uint16_t command_credits;
+
+/* De-queues the FM CMD from the TX_Q */
+void dequeue_fm_tx_cmd()
+{
+ TX_Q *new_first, *new_last;
+ static int cmd_count = 0;
+ static uint8_t credits = 0;
+ uint8_t i;
+ uint8_t temp_1 = 0x11;
+
+ if (cmd_count >= MAX_FM_CMD_CNT) {
+ ALOGI("\n\n\t\tReached Max. CMD COUNT!!\n\n");
+ lib_running = 0;
+ return;
+ }
+
+ /*
+ * Save the 'first' pointer and make it NULL.
+ * This is to allow the FM-HAL to enqueue more CMDs to the TX_Q
+ * without having to contend for the 'tx_q_lock' with the FM-HCI thread.
+ * Once the pointer to the 'first' element in the TX_Q is available,
+ * send all the commands in the queue to WCNSS FILTER based on the
+ * command credits provided by the Controller. If command credits are
+ * not available, then wait for the same.
+ */
+ pthread_mutex_lock(&fmHCIControlBlock.tx_q_lock);
+ if (!fmHCIControlBlock.first) {
+ ALOGI("No FM CMD available in the Q\n");
+ pthread_mutex_unlock(&fmHCIControlBlock.tx_q_lock);
+ return;
+ }
+ else {
+ new_first = fmHCIControlBlock.first;
+ new_last = fmHCIControlBlock.last;
+ fmHCIControlBlock.first = fmHCIControlBlock.last = NULL;
+ }
+ pthread_mutex_unlock(&fmHCIControlBlock.tx_q_lock);
+
+ //credits = command_credits;
+
+ TX_Q *temp = new_first;
+ while(temp != NULL) {
+
+wait_for_cmd_credits:
+ pthread_mutex_lock(&fmHCIControlBlock.credit_lock);
+ while (command_credits == 0) {
+ ALOGI("\n\n\t\tWaiting for COMMAND CREDITS from CONTROLLER\n\n");
+ pthread_cond_wait(&fmHCIControlBlock.cmd_credits_cond, &fmHCIControlBlock.credit_lock);
+ }
+ pthread_mutex_unlock(&fmHCIControlBlock.credit_lock);
+
+ /* Check if we really got the command credits */
+ //REVISIT this area
+ //if (credits) {
+ if (command_credits) {
+ ALOGI("%s: Sending the FM-CMD(prot_byte: 0x%x): 0x%x dequeued from TX_Q\n", __func__, temp->hdr->protocol_byte, temp->hdr->opcode);
+
+ if (temp->hdr->plen) {
+ ALOGI("%s: CMD-PARAMS:", __func__);
+ for (i = 0; i < temp->hdr->plen; i++)
+ ALOGI(" <0x%x> ", temp->hdr->cmd_params[i]);
+ } else
+ ALOGE("%s: NO CMD-PARAMS available for this command", __func__);
+
+ ALOGE("%s: Sizeof FM_HDR: %d", __func__, (int)sizeof(temp->hdr));
+ /* Use socket 'fd' to send the command down to WCNSS Filter */
+ write(fd, (uint8_t *)temp->hdr, (sizeof(FM_HDR) + temp->hdr->plen));
+ //write(fd, &temp_1, 1);
+
+ /* Decrement cmd credits by '1' after sending the cmd*/
+ command_credits--;
+
+ /* TODO:
+ * Initialize 'cmd_cnt' to MAX_FM_CMD(?). Should we have any limit on the
+ * number of outstanding commands in the TX-Q ??
+ */
+ cmd_count--;
+
+ /* Fetch the next cmd to be sent */
+ temp = temp->next;
+ } else {
+ if (!lib_running)
+ break;
+
+ ALOGI("\n\n\t\tFalse wakeup: Yet to get COMMAND CREDITS from CONTROLLER\n\n");
+ goto wait_for_cmd_credits;
+ }
+ }
+}
+
+
+static int event_fd = -1;
+
+static inline int add_event_fd(fd_set *set) {
+ if (event_fd == -1) {
+ event_fd = eventfd(0, 0);
+ if (event_fd == -1) {
+ ALOGE("%s unable to create event fd: %s", __func__, strerror(errno));
+ return -1;
+ }
+ }
+
+ FD_SET(event_fd, set);
+ return event_fd;
+}
+
+static inline uint64_t read_event() {
+ assert(event_fd != -1);
+
+ uint64_t value = 0;
+ eventfd_read(event_fd, &value);
+ return value;
+}
+
+static int read_fm_event(int fd, FM_EVT_HDR *pbuf, int len)
+{
+ fd_set readFds;
+ int n = 0, ret = -1, evt_type = -1, evt_len = -1;
+
+ while (lib_running)
+ {
+ FD_ZERO(&readFds);
+ FD_SET(fd, &readFds);
+
+ if (event_fd == -1) {
+ event_fd = eventfd(0, 0);
+ if (event_fd == -1) {
+ ALOGE("%s: unable to create event fd: %s", __func__, strerror(errno));
+ return -1;
+ }
+ }
+ FD_SET(event_fd, &readFds);
+ int fd_max = (event_fd > fd ? event_fd : fd);
+
+ ALOGE("%s: Waiting for events from WCNSS FILTER...\n", __func__);
+
+ /* Wait for event/data from WCNSS Filter */
+ n = select(fd_max+1, &readFds, NULL, NULL, NULL);
+ if (n > 0)
+ {
+ /* Check if event is available or not */
+#if 1
+ if (FD_ISSET(fd, &readFds)) {
+ ret = read(fd, (uint8_t *)pbuf, (size_t)(sizeof(FM_EVT_HDR) + MAX_FM_EVT_PARAMS));
+ if (0 == ret) {
+ ALOGE("%s: read() returned '0' bytes\n", __func__);
+ }
+ else {
+ ALOGE("%s: read() returned %d bytes of FM event/data\n", __func__, ret);
+ while (ret > 0) {
+ if (pbuf->evt_code == FM_CMD_COMPLETE) {
+ ALOGE("\n\t%s: Received %d bytes of CC event data from WCNSS FILTER!!!\n\t"
+ "Evt type\t: 0x%x \n\tEvt Code\t: 0x%x \n\tEvt len\t\t: 0x%x \n\topcode\t\t: 0x%x%x \n\tCmd Credits\t: 0x%x \n\tStatus\t\t: 0x%x\n",
+ __func__, ret, pbuf->protocol_byte, pbuf->evt_code, pbuf->evt_len, pbuf->cmd_params[2], pbuf->cmd_params[1],
+ pbuf->cmd_params[0], pbuf->cmd_params[3]);
+ evt_type = FM_CMD_COMPLETE;
+ } else if (pbuf->evt_code == FM_CMD_STATUS) {
+ ALOGE("\n\t%s: Received %d bytes of CS event data from WCNSS FILTER!!!\n\t"
+ "Evt type\t: 0x%x \n\tEvt Code\t: 0x%x \n\tEvt len\t\t: 0x%x \n\topcode\t\t: 0x%x%x \n\tCmd Credits\t: 0x%x \n\tStatus\t\t: 0x%x\n",
+ __func__, ret, pbuf->protocol_byte, pbuf->evt_code, pbuf->evt_len, pbuf->cmd_params[3], pbuf->cmd_params[2],
+ pbuf->cmd_params[1], pbuf->cmd_params[0]);
+ evt_type = FM_CMD_STATUS;
+ } else {
+ ALOGI("%s: Not CS/CC Event: Recvd. Event Code: 0x%2x", __func__, pbuf->evt_code);
+ evt_type = -1;
+ }
+
+ evt_len = pbuf->evt_len;
+
+ /* Notify 'fmHCITask' about availability of event or data */
+ ALOGE("%s: \nNotifying 'fmHCITask' availability of FM event or data...\n", __func__);
+ event_notification(HC_EVENT_RX);
+
+ if (hal_cb && hal_cb->fm_evt_notify != NULL)
+ hal_cb->fm_evt_notify((uint8_t *)pbuf);
+ else
+ ALOGE("%s: ASSERT $$$$$$ Callback function NULL $$$$$", __func__);
+
+ if((evt_type == FM_CMD_STATUS) || (evt_type == FM_CMD_COMPLETE)) {
+ /* Provide command credits to allow fmHCITask to send cmds */
+ pthread_mutex_lock(&fmHCIControlBlock.credit_lock);
+ if (evt_type == FM_CMD_COMPLETE) {
+ ALOGE("\n%s: Command Credit(s): '%d' received as part of CC Event for FM-CMD: 0x%x%x \n", __func__, pbuf->cmd_params[0],
+ pbuf->cmd_params[2], pbuf->cmd_params[1]);
+ command_credits = pbuf->cmd_params[0];
+ } else if (evt_type == FM_CMD_STATUS) {
+ ALOGE("\n%s: Command Credit(s): '%d' received as part of CS Event for FM-CMD: 0x%x%x \n", __func__, pbuf->cmd_params[1],
+ pbuf->cmd_params[3], pbuf->cmd_params[2]);
+ command_credits = pbuf->cmd_params[1];
+ }
+ pthread_cond_signal(&fmHCIControlBlock.cmd_credits_cond);
+ pthread_mutex_unlock(&fmHCIControlBlock.credit_lock);
+ }
+
+ ret = ret - (evt_len + 3);
+ ALOGE("%s: Length of available bytes @ HCI Layer: %d", __func__, ret);
+
+ if (ret > 0) {
+ ALOGE("%s: Remaining bytes of event/data: %d", __func__, ret);
+ pbuf = (FM_EVT_HDR *)&pbuf->cmd_params[evt_len];
+ ALOGE("%s: Protocol byte of next packet: 0x%2x", __func__, pbuf[0]);
+ }
+
+ }
+ } //end of processing the event
+
+ } else
+ ALOGE("%s: No data available, though select returned!!!\n", __func__);
+#endif
+ }
+ else if (n < 0)
+ ALOGE("%s: select() failed with return value: %d", __func__, ret);
+ else if (n == 0)
+ ALOGE("%s: select() timeout!!!", __func__);
+ }
+
+ return ret;
+}
+
+static void *userial_read_thread(void *arg)
+{
+ int length;
+
+ while(lib_running) {
+
+ FM_EVT_HDR *evt_buf = (FM_EVT_HDR *) malloc(sizeof(FM_EVT_HDR) + MAX_FM_EVT_PARAMS);
+
+ ALOGE("%s: Wait for events from the WCNSS Filter", __func__);
+ length = read_fm_event(fd, evt_buf, sizeof(FM_EVT_HDR) + MAX_FM_EVT_PARAMS);
+
+#if 0
+ if (length > 0) {
+ //TODO: Copy the data to the RX-Q
+ //TODO: Notify event/data availability
+ ALOGE("%s: FM Event or RDS data available: Notify FM-HAL Layer", __func__);
+ event_notification(HC_EVENT_RX);
+ } else
+ ALOGE("%s: return value from read_fm_event(): %d", __func__, length);
+#endif
+ //TODO: Have condition for breaking from the loop!
+ }
+ lib_running = 0;
+ ALOGE("%s: Leaving userial_read_thread()", __func__);
+ pthread_exit(NULL);
+
+ return arg;
+}
+
+/*
+ * Reads the FM-CMDs from the TX_Q and sends it down to WCNSS Filter
+ * Reads events sent by the WCNSS Filter and copies onto RX_Q
+ */
+static void* fmHCITask(void *arg)
+{
+ static uint16_t events;
+
+ while (lib_running) {
+ pthread_mutex_lock(&fmHCIControlBlock.event_lock);
+ while (ready_events == 0) {
+ pthread_cond_wait(&fmHCIControlBlock.event_cond, &fmHCIControlBlock.event_lock);
+ }
+
+ events = ready_events;
+ ready_events = 0;
+ pthread_mutex_unlock(&fmHCIControlBlock.event_lock);
+
+ if ((events & 0xFFF8) == HC_EVENT_TX) {
+ ALOGI("\n@@@@@ FM-HCI Task : EVENT_TX available @@@@@\n");
+ dequeue_fm_tx_cmd();
+ }
+ if ((events & 0xFFF4) == HC_EVENT_RX) {
+ ALOGI("\n##### FM-HCI Task : EVENT_RX available #####\n");
+ //TODO: Notify FM-HAL about event/data availablity
+ }
+ }
+ ALOGE("%s: ##### Exiting fmHCITask Worker thread!!! #####", __func__);
+ return arg;
+}
+
+int fm_hci_init(fm_hal_cb *p_cb)
+{
+ pthread_attr_t thread_attr;
+ struct sched_param param;
+ int policy, result, hci_type;
+
+ ALOGI("FM-HCI: init");
+
+ /* Save the FM-HAL event notofication callback func. */
+ hal_cb = p_cb;
+
+ ALOGI("FM-HCI: Loading the WCNSS HAL library...");
+ init_vnd_if();
+
+ ALOGI("%s: Initializing FM-HCI layer...", __func__);
+ lib_running = 1;
+ ready_events = 0;
+ command_credits = 1;
+
+ pthread_mutex_init(&fmHCIControlBlock.tx_q_lock, NULL);
+ pthread_mutex_init(&fmHCIControlBlock.credit_lock, NULL);
+ pthread_mutex_init(&fmHCIControlBlock.event_lock, NULL);
+
+ pthread_cond_init(&fmHCIControlBlock.event_cond, NULL);
+ pthread_cond_init(&fmHCIControlBlock.cmd_credits_cond, NULL);
+
+ pthread_attr_init(&thread_attr);
+
+ ALOGI("FM-HCI: Creating the FM-HCI TASK...");
+ if (pthread_create(&fmHCIControlBlock.fmHCITaskThreadId, &thread_attr, \
+ fmHCITask, NULL) != 0)
+ {
+ ALOGE("pthread_create failed!");
+ lib_running = 0;
+ return FM_HC_STATUS_FAIL;
+ }
+
+ ALOGI("FM-HCI: Configuring the scheduling policy and priority of the FM HCI thread");
+ if(pthread_getschedparam(fmHCIControlBlock.fmHCITaskThreadId, &policy, ¶m)==0)
+ {
+ policy = SCHED_NORMAL;
+#if (BTHC_LINUX_BASE_POLICY!=SCHED_NORMAL)
+ param.sched_priority = BTHC_MAIN_THREAD_PRIORITY;
+#endif
+ result = pthread_setschedparam(fmHCIControlBlock.fmHCITaskThreadId, policy, ¶m);
+ if (result != 0)
+ {
+ ALOGW("libbt-hci init: pthread_setschedparam failed (%s)", \
+ strerror(result));
+ }
+ } else
+ ALOGI("FM-HCI: Failed to get the Scheduling parameters!!!");
+
+ return FM_HC_STATUS_SUCCESS;
+}
+
+
+void fm_power(fm_power_state state)
+{
+ int val;
+
+ if (state) {
+ ALOGI("FM-HCI: %s: Turning FM ON", __func__);
+ val = state;
+ fm_vnd_if->op(FM_VND_OP_POWER_CTRL, &val);
+ } else {
+ ALOGI("FM-HCI: %s: Turning FM OFF", __func__);
+ val = state;
+ fm_vnd_if->op(FM_VND_OP_POWER_CTRL, &val);
+ }
+}
+
+#define CH_MAX 3
+int open_serial_port()
+{
+ 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 = fm_vnd_if->op(BT_VND_OP_FM_USERIAL_OPEN, &fd_array);
+
+ fd = fd_array[0];
+ if (fd == -1) {
+ ALOGE("%s unable to open TTY serial port", __func__);
+ goto err;
+ }
+
+ //TODO: Start the userial read thread here
+ ALOGE("%s: Starting the userial read thread....", __func__);
+ if (pthread_create(&fmHCIControlBlock.fmRxTaskThreadId, NULL, \
+ userial_read_thread, NULL) != 0)
+ {
+ ALOGE("pthread_create failed!");
+ lib_running = 0;
+ return FM_HC_STATUS_FAIL;
+ }
+
+ return 0;
+
+err:
+ ALOGI("%s: Closing the TTy Serial port due to error!!!", __func__);
+ ret = fm_vnd_if->op(BT_VND_OP_FM_USERIAL_CLOSE, NULL);
+ return -1;
+}
+
+void enqueue_fm_tx_cmd(FM_HDR *pbuf)
+{
+
+ pthread_mutex_lock(&fmHCIControlBlock.tx_q_lock);
+
+ if (!fmHCIControlBlock.first) {
+ fmHCIControlBlock.first = (TX_Q *) malloc(sizeof(TX_Q));
+ if (!fmHCIControlBlock.first) {
+ printf("Failed to allocate memory for first!!\n");
+ pthread_mutex_unlock(&fmHCIControlBlock.tx_q_lock);
+ return;
+ }
+ fmHCIControlBlock.first->hdr = pbuf;
+ fmHCIControlBlock.first->next = NULL;
+ fmHCIControlBlock.last = fmHCIControlBlock.first;
+ ALOGI("%s: FM-CMD ENQUEUED SUCCESSFULLY", __func__);
+ } else {
+ TX_Q *element = (TX_Q *) malloc(sizeof(TX_Q));
+ if (!element) {
+ printf("Failed to allocate memory for element!!\n");
+ pthread_mutex_unlock(&fmHCIControlBlock.tx_q_lock);
+ return;
+ }
+ fmHCIControlBlock.last->next = element;
+ element->hdr = pbuf;
+ element->next = NULL;
+ fmHCIControlBlock.last = element;
+ ALOGI("%s: fm-cmd enqueued successfully", __func__);
+ }
+
+ pthread_mutex_unlock(&fmHCIControlBlock.tx_q_lock);
+}
+
+/** Transmit frame */
+void transmit(FM_HDR *pbuf)
+{
+ enqueue_fm_tx_cmd(pbuf);
+ event_notification(HC_EVENT_TX);
+}
diff --git a/fm_hci/fm_hci.h b/fm_hci/fm_hci.h
new file mode 100644
index 0000000..585411e
--- /dev/null
+++ b/fm_hci/fm_hci.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __FM_HCI__
+#define __FM_HCI__
+
+#pragma pack(1)
+
+#include <radio-helium.h>
+
+/** Host/Controller Library Return Status */
+typedef enum {
+ FM_HC_STATUS_SUCCESS,
+ FM_HC_STATUS_FAIL,
+ FM_HC_STATUS_NOT_READY,
+ FM_HC_STATUS_NOMEM,
+ FM_HC_STATUS_BUSY,
+ FM_HC_STATUS_CORRUPTED_BUFFER
+} fm_hc_status_t;
+
+typedef enum {
+ FM_RADIO_DISABLE,
+ FM_RADIO_ENABLE
+}fm_power_state;
+
+/* Host/Controller lib internal event ID */
+#define HC_EVENT_PRELOAD 0x0001
+#define HC_EVENT_POSTLOAD 0x0002
+#define HC_EVENT_RX 0x0004
+#define HC_EVENT_TX 0x0008
+#define HC_EVENT_LPM_ENABLE 0x0010
+#define HC_EVENT_LPM_DISABLE 0x0020
+#define HC_EVENT_LPM_WAKE_DEVICE 0x0040
+#define HC_EVENT_LPM_ALLOW_SLEEP 0x0080
+#define HC_EVENT_LPM_IDLE_TIMEOUT 0x0100
+#define HC_EVENT_EXIT 0x0200
+#define HC_EVENT_EPILOG 0x0400
+
+#define MAX_FM_CMD_CNT 100
+#define FM_CMD 0x11
+#define FM_EVT 0x14
+#define MAX_FM_EVT_PARAMS 255
+
+#define FM_CMD_COMPLETE 0x0f
+#define FM_CMD_STATUS 0x10
+
+static pthread_mutex_t utils_mutex;
+
+static volatile uint8_t lib_running = 0;
+static volatile uint16_t ready_events = 0;
+
+FILE *fd_wcnss_filter;
+
+typedef struct {
+ uint8_t protocol_byte;
+ uint16_t opcode;
+ uint8_t plen;
+ uint8_t cmd_params[];
+} FM_HDR;
+
+typedef struct {
+ uint8_t protocol_byte;
+ uint8_t evt_code;
+ uint8_t evt_len;
+ uint8_t cmd_params[];
+} FM_EVT_HDR;
+
+typedef struct hdr
+{
+ FM_HDR *hdr;
+ struct hdr *next;
+} TX_Q;
+
+void transmit(FM_HDR *pbuf);
+int fm_hci_init(fm_hal_cb *);
+void fm_power(fm_power_state state);
+int open_serial_port(void);
+
+typedef struct {
+ pthread_mutex_t tx_q_lock;
+ pthread_mutex_t credit_lock;
+ pthread_mutex_t event_lock;
+
+ pthread_cond_t event_cond;
+ pthread_cond_t cmd_credits_cond;
+
+ pthread_t fmHALTaskThreadId;
+ pthread_t fmHCITaskThreadId;
+ pthread_t fmRxTaskThreadId;
+
+ TX_Q *first;
+ TX_Q *last;
+
+} fmHCIControlStructure;
+
+fmHCIControlStructure fmHCIControlBlock;
+
+#endif
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