summaryrefslogtreecommitdiffstats
path: root/hci/src
diff options
context:
space:
mode:
Diffstat (limited to 'hci/src')
-rw-r--r--hci/src/bt_hci_bdroid.c505
-rw-r--r--hci/src/bt_hw.c207
-rwxr-xr-xhci/src/btsnoop.c694
-rw-r--r--hci/src/hci_h4.c1075
-rwxr-xr-xhci/src/hci_mct.c1179
-rw-r--r--hci/src/lpm.c425
-rw-r--r--hci/src/userial.c530
-rw-r--r--hci/src/userial_mct.c439
-rw-r--r--hci/src/utils.c308
9 files changed, 5362 insertions, 0 deletions
diff --git a/hci/src/bt_hci_bdroid.c b/hci/src/bt_hci_bdroid.c
new file mode 100644
index 0000000..7b14eb3
--- /dev/null
+++ b/hci/src/bt_hci_bdroid.c
@@ -0,0 +1,505 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2009-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * Filename: bt_hci_bdroid.c
+ *
+ * Description: Bluedroid Bluetooth Host/Controller interface library
+ * implementation
+ *
+ ******************************************************************************/
+
+#define LOG_TAG "bt_hci_bdroid"
+
+#include <utils/Log.h>
+#include <pthread.h>
+#include "bt_hci_bdroid.h"
+#include "bt_vendor_lib.h"
+#include "utils.h"
+#include "hci.h"
+#include "userial.h"
+#include "bt_utils.h"
+#include <sys/prctl.h>
+
+#ifndef BTHC_DBG
+#define BTHC_DBG FALSE
+#endif
+
+#if (BTHC_DBG == TRUE)
+#define BTHCDBG(param, ...) {ALOGD(param, ## __VA_ARGS__);}
+#else
+#define BTHCDBG(param, ...) {}
+#endif
+
+/******************************************************************************
+** Externs
+******************************************************************************/
+
+extern bt_vendor_interface_t *bt_vnd_if;
+extern int num_hci_cmd_pkts;
+void lpm_init(void);
+void lpm_cleanup(void);
+void lpm_enable(uint8_t turn_on);
+void lpm_wake_deassert(void);
+void lpm_allow_bt_device_sleep(void);
+void lpm_wake_assert(void);
+void init_vnd_if(unsigned char *local_bdaddr);
+void btsnoop_open(char *p_path);
+void btsnoop_close(void);
+
+/******************************************************************************
+** Variables
+******************************************************************************/
+
+bt_hc_callbacks_t *bt_hc_cbacks = NULL;
+BUFFER_Q tx_q;
+tHCI_IF *p_hci_if;
+
+/******************************************************************************
+** Local type definitions
+******************************************************************************/
+
+/* Host/Controller lib thread control block */
+typedef struct
+{
+ pthread_t worker_thread;
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+} bt_hc_cb_t;
+
+/******************************************************************************
+** Static Variables
+******************************************************************************/
+
+static bt_hc_cb_t hc_cb;
+static volatile uint8_t lib_running = 0;
+static volatile uint16_t ready_events = 0;
+static volatile uint8_t tx_cmd_pkts_pending = FALSE;
+
+/******************************************************************************
+** Functions
+******************************************************************************/
+
+static void *bt_hc_worker_thread(void *arg);
+
+void bthc_signal_event(uint16_t event)
+{
+ pthread_mutex_lock(&hc_cb.mutex);
+ ready_events |= event;
+ pthread_cond_signal(&hc_cb.cond);
+ pthread_mutex_unlock(&hc_cb.mutex);
+}
+
+/*****************************************************************************
+**
+** BLUETOOTH HOST/CONTROLLER INTERFACE LIBRARY FUNCTIONS
+**
+*****************************************************************************/
+
+static int init(const bt_hc_callbacks_t* p_cb, unsigned char *local_bdaddr)
+{
+ pthread_attr_t thread_attr;
+ struct sched_param param;
+ int policy, result;
+
+ ALOGI("init");
+
+ if (p_cb == NULL)
+ {
+ ALOGE("init failed with no user callbacks!");
+ return BT_HC_STATUS_FAIL;
+ }
+
+ /* store reference to user callbacks */
+ bt_hc_cbacks = (bt_hc_callbacks_t *) p_cb;
+
+ init_vnd_if(local_bdaddr);
+
+ utils_init();
+#ifdef HCI_USE_MCT
+ extern tHCI_IF hci_mct_func_table;
+ p_hci_if = &hci_mct_func_table;
+#else
+ extern tHCI_IF hci_h4_func_table;
+ p_hci_if = &hci_h4_func_table;
+#endif
+
+ p_hci_if->init();
+
+ userial_init();
+ lpm_init();
+
+ utils_queue_init(&tx_q);
+
+ if (lib_running)
+ {
+ ALOGW("init has been called repeatedly without calling cleanup ?");
+ }
+
+ lib_running = 1;
+ ready_events = 0;
+ pthread_mutex_init(&hc_cb.mutex, NULL);
+ pthread_cond_init(&hc_cb.cond, NULL);
+ pthread_attr_init(&thread_attr);
+
+ if (pthread_create(&hc_cb.worker_thread, &thread_attr, \
+ bt_hc_worker_thread, NULL) != 0)
+ {
+ ALOGE("pthread_create failed!");
+ lib_running = 0;
+ return BT_HC_STATUS_FAIL;
+ }
+
+ if(pthread_getschedparam(hc_cb.worker_thread, &policy, &param)==0)
+ {
+ policy = BTHC_LINUX_BASE_POLICY;
+#if (BTHC_LINUX_BASE_POLICY!=SCHED_NORMAL)
+ param.sched_priority = BTHC_MAIN_THREAD_PRIORITY;
+#endif
+ result = pthread_setschedparam(hc_cb.worker_thread, policy, &param);
+ if (result != 0)
+ {
+ ALOGW("libbt-hci init: pthread_setschedparam failed (%s)", \
+ strerror(result));
+ }
+ }
+
+ return BT_HC_STATUS_SUCCESS;
+}
+
+
+/** Chip power control */
+static void set_power(bt_hc_chip_power_state_t state)
+{
+ int pwr_state;
+
+ BTHCDBG("set_power %d", state);
+
+ /* Calling vendor-specific part */
+ pwr_state = (state == BT_HC_CHIP_PWR_ON) ? BT_VND_PWR_ON : BT_VND_PWR_OFF;
+
+ if (bt_vnd_if)
+ bt_vnd_if->op(BT_VND_OP_POWER_CTRL, &pwr_state);
+ else
+ ALOGE("vendor lib is missing!");
+}
+
+
+/** Configure low power mode wake state */
+static int lpm(bt_hc_low_power_event_t event)
+{
+ uint8_t status = TRUE;
+
+ switch (event)
+ {
+ case BT_HC_LPM_DISABLE:
+ bthc_signal_event(HC_EVENT_LPM_DISABLE);
+ break;
+
+ case BT_HC_LPM_ENABLE:
+ bthc_signal_event(HC_EVENT_LPM_ENABLE);
+ break;
+
+ case BT_HC_LPM_WAKE_ASSERT:
+ bthc_signal_event(HC_EVENT_LPM_WAKE_DEVICE);
+ break;
+
+ case BT_HC_LPM_WAKE_DEASSERT:
+ bthc_signal_event(HC_EVENT_LPM_ALLOW_SLEEP);
+ break;
+ }
+
+ return(status == TRUE) ? BT_HC_STATUS_SUCCESS : BT_HC_STATUS_FAIL;
+}
+
+
+/** Called prio to stack initialization */
+static void preload(TRANSAC transac)
+{
+ BTHCDBG("preload");
+ bthc_signal_event(HC_EVENT_PRELOAD);
+}
+
+
+/** Called post stack initialization */
+static void postload(TRANSAC transac)
+{
+ BTHCDBG("postload");
+ bthc_signal_event(HC_EVENT_POSTLOAD);
+}
+
+
+/** Transmit frame */
+static int transmit_buf(TRANSAC transac, char *p_buf, int len)
+{
+ utils_enqueue(&tx_q, (void *) transac);
+
+ bthc_signal_event(HC_EVENT_TX);
+
+ return BT_HC_STATUS_SUCCESS;
+}
+
+
+/** Controls receive flow */
+static int set_rxflow(bt_rx_flow_state_t state)
+{
+ BTHCDBG("set_rxflow %d", state);
+
+ userial_ioctl(\
+ ((state == BT_RXFLOW_ON) ? USERIAL_OP_RXFLOW_ON : USERIAL_OP_RXFLOW_OFF), \
+ NULL);
+
+ return BT_HC_STATUS_SUCCESS;
+}
+
+
+/** Controls HCI logging on/off */
+static int logging(bt_hc_logging_state_t state, char *p_path)
+{
+ BTHCDBG("logging %d", state);
+
+ if (state == BT_HC_LOGGING_ON)
+ {
+ if (p_path != NULL)
+ btsnoop_open(p_path);
+ }
+ else
+ {
+ btsnoop_close();
+ }
+
+ return BT_HC_STATUS_SUCCESS;
+}
+
+
+/** Closes the interface */
+static void cleanup( void )
+{
+ BTHCDBG("cleanup");
+
+ if (lib_running)
+ {
+ lib_running = 0;
+ bthc_signal_event(HC_EVENT_EXIT);
+ pthread_join(hc_cb.worker_thread, NULL);
+ }
+
+ lpm_cleanup();
+ userial_close();
+ p_hci_if->cleanup();
+ utils_cleanup();
+
+ /* Calling vendor-specific part */
+ if (bt_vnd_if)
+ bt_vnd_if->cleanup();
+
+ bt_hc_cbacks = NULL;
+}
+
+
+static const bt_hc_interface_t bluetoothHCLibInterface = {
+ sizeof(bt_hc_interface_t),
+ init,
+ set_power,
+ lpm,
+ preload,
+ postload,
+ transmit_buf,
+ set_rxflow,
+ logging,
+ cleanup
+};
+
+
+/*******************************************************************************
+**
+** Function bt_hc_worker_thread
+**
+** Description Mian worker thread
+**
+** Returns void *
+**
+*******************************************************************************/
+static void *bt_hc_worker_thread(void *arg)
+{
+ uint16_t events;
+ HC_BT_HDR *p_msg, *p_next_msg;
+
+ ALOGI("bt_hc_worker_thread started");
+ prctl(PR_SET_NAME, (unsigned long)"bt_hc_worker", 0, 0, 0);
+ tx_cmd_pkts_pending = FALSE;
+
+ raise_priority_a2dp(TASK_HIGH_HCI_WORKER);
+
+ while (lib_running)
+ {
+ pthread_mutex_lock(&hc_cb.mutex);
+ while (ready_events == 0)
+ {
+ pthread_cond_wait(&hc_cb.cond, &hc_cb.mutex);
+ }
+ events = ready_events;
+ ready_events = 0;
+ pthread_mutex_unlock(&hc_cb.mutex);
+
+#ifndef HCI_USE_MCT
+ if (events & HC_EVENT_RX)
+ {
+ p_hci_if->rcv();
+
+ if ((tx_cmd_pkts_pending == TRUE) && (num_hci_cmd_pkts > 0))
+ {
+ /* Got HCI Cmd Credits from Controller.
+ * Prepare to send prior pending Cmd packets in the
+ * following HC_EVENT_TX session.
+ */
+ events |= HC_EVENT_TX;
+ }
+ }
+#endif
+
+ if (events & HC_EVENT_PRELOAD)
+ {
+ userial_open(USERIAL_PORT_1);
+
+ /* Calling vendor-specific part */
+ if (bt_vnd_if)
+ {
+ bt_vnd_if->op(BT_VND_OP_FW_CFG, NULL);
+ }
+ else
+ {
+ if (bt_hc_cbacks)
+ bt_hc_cbacks->preload_cb(NULL, BT_HC_PRELOAD_FAIL);
+ }
+ }
+
+ if (events & HC_EVENT_POSTLOAD)
+ {
+ /* Start from SCO related H/W configuration, if SCO configuration
+ * is required. Then, follow with reading requests of getting
+ * ACL data length for both BR/EDR and LE.
+ */
+ int result = -1;
+
+ /* Calling vendor-specific part */
+ if (bt_vnd_if)
+ result = bt_vnd_if->op(BT_VND_OP_SCO_CFG, NULL);
+
+ if (result == -1)
+ p_hci_if->get_acl_max_len();
+ }
+
+ if (events & HC_EVENT_TX)
+ {
+ /*
+ * We will go through every packets in the tx queue.
+ * Fine to clear tx_cmd_pkts_pending.
+ */
+ tx_cmd_pkts_pending = FALSE;
+ HC_BT_HDR * sending_msg_que[64];
+ int sending_msg_count = 0;
+ utils_lock();
+ p_next_msg = tx_q.p_first;
+ while (p_next_msg && sending_msg_count <
+ (int)sizeof(sending_msg_que)/sizeof(sending_msg_que[0]))
+ {
+ if ((p_next_msg->event & MSG_EVT_MASK)==MSG_STACK_TO_HC_HCI_CMD)
+ {
+ /*
+ * if we have used up controller's outstanding HCI command
+ * credits (normally is 1), skip all HCI command packets in
+ * the queue.
+ * The pending command packets will be sent once controller
+ * gives back us credits through CommandCompleteEvent or
+ * CommandStatusEvent.
+ */
+ if ((tx_cmd_pkts_pending == TRUE) || (num_hci_cmd_pkts <= 0))
+ {
+ tx_cmd_pkts_pending = TRUE;
+ p_next_msg = utils_getnext(p_next_msg);
+ continue;
+ }
+ }
+
+ p_msg = p_next_msg;
+ p_next_msg = utils_getnext(p_msg);
+ utils_remove_from_queue_unlocked(&tx_q, p_msg);
+ sending_msg_que[sending_msg_count++] = p_msg;
+ }
+ utils_unlock();
+ int i;
+ for(i = 0; i < sending_msg_count; i++)
+ p_hci_if->send(sending_msg_que[i]);
+ if (tx_cmd_pkts_pending == TRUE)
+ BTHCDBG("Used up Tx Cmd credits");
+
+ }
+
+ if (events & HC_EVENT_LPM_ENABLE)
+ {
+ lpm_enable(TRUE);
+ }
+
+ if (events & HC_EVENT_LPM_DISABLE)
+ {
+ lpm_enable(FALSE);
+ }
+
+ if (events & HC_EVENT_LPM_IDLE_TIMEOUT)
+ {
+ lpm_wake_deassert();
+ }
+
+ if (events & HC_EVENT_LPM_ALLOW_SLEEP)
+ {
+ lpm_allow_bt_device_sleep();
+ }
+
+ if (events & HC_EVENT_LPM_WAKE_DEVICE)
+ {
+ lpm_wake_assert();
+ }
+
+ if (events & HC_EVENT_EXIT)
+ break;
+ }
+
+ ALOGI("bt_hc_worker_thread exiting");
+
+ pthread_exit(NULL);
+
+ return NULL; // compiler friendly
+}
+
+
+/*******************************************************************************
+**
+** Function bt_hc_get_interface
+**
+** Description Caller calls this function to get API instance
+**
+** Returns API table
+**
+*******************************************************************************/
+const bt_hc_interface_t *bt_hc_get_interface(void)
+{
+ return &bluetoothHCLibInterface;
+}
+
diff --git a/hci/src/bt_hw.c b/hci/src/bt_hw.c
new file mode 100644
index 0000000..b2a099f
--- /dev/null
+++ b/hci/src/bt_hw.c
@@ -0,0 +1,207 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2009-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * Filename: bt_hw.c
+ *
+ * Description: Bluedroid libbt-vendor callback functions
+ *
+ ******************************************************************************/
+
+#define LOG_TAG "bt_hw"
+
+#include <dlfcn.h>
+#include <utils/Log.h>
+#include <pthread.h>
+#include "bt_vendor_lib.h"
+#include "bt_hci_bdroid.h"
+#include "hci.h"
+#include "userial.h"
+
+/******************************************************************************
+** Externs
+******************************************************************************/
+
+extern tHCI_IF *p_hci_if;
+void lpm_vnd_cback(uint8_t vnd_result);
+
+/******************************************************************************
+** Variables
+******************************************************************************/
+
+bt_vendor_interface_t *bt_vnd_if=NULL;
+
+/******************************************************************************
+** Functions
+******************************************************************************/
+
+/******************************************************************************
+**
+** Function fwcfg_cb
+**
+** Description HOST/CONTROLLER VENDOR LIB CALLBACK API - This function is
+** called when the libbt-vendor completed firmware
+** configuration process
+**
+** Returns None
+**
+******************************************************************************/
+static void fwcfg_cb(bt_vendor_op_result_t result)
+{
+ bt_hc_postload_result_t status = (result == BT_VND_OP_RESULT_SUCCESS) ? \
+ BT_HC_PRELOAD_SUCCESS : BT_HC_PRELOAD_FAIL;
+
+ if (bt_hc_cbacks)
+ bt_hc_cbacks->preload_cb(NULL, status);
+}
+
+/******************************************************************************
+**
+** Function scocfg_cb
+**
+** Description HOST/CONTROLLER VENDOR LIB CALLBACK API - This function is
+** called when the libbt-vendor completed vendor specific SCO
+** configuration process
+**
+** Returns None
+**
+******************************************************************************/
+static void scocfg_cb(bt_vendor_op_result_t result)
+{
+ /* Continue rest of postload process*/
+ p_hci_if->get_acl_max_len();
+}
+
+/******************************************************************************
+**
+** Function lpm_vnd_cb
+**
+** Description HOST/CONTROLLER VENDOR LIB CALLBACK API - This function is
+** called back from the libbt-vendor to indicate the result of
+** previous LPM enable/disable request
+**
+** Returns None
+**
+******************************************************************************/
+static void lpm_vnd_cb(bt_vendor_op_result_t result)
+{
+ uint8_t status = (result == BT_VND_OP_RESULT_SUCCESS) ? 0 : 1;
+
+ lpm_vnd_cback(status);
+}
+
+/******************************************************************************
+**
+** Function alloc
+**
+** Description HOST/CONTROLLER VENDOR LIB CALLOUT API - This function is
+** called from the libbt-vendor to request for data buffer
+** allocation
+**
+** Returns NULL / pointer to allocated buffer
+**
+******************************************************************************/
+static void *alloc(int size)
+{
+ HC_BT_HDR *p_hdr = NULL;
+
+ if (bt_hc_cbacks)
+ p_hdr = (HC_BT_HDR *) bt_hc_cbacks->alloc(size);
+
+ return (p_hdr);
+}
+
+/******************************************************************************
+**
+** Function dealloc
+**
+** Description HOST/CONTROLLER VENDOR LIB CALLOUT API - This function is
+** called from the libbt-vendor to release the data buffer
+** allocated through the alloc call earlier
+**
+** Returns None
+**
+******************************************************************************/
+static void dealloc(void *p_buf)
+{
+ HC_BT_HDR *p_hdr = (HC_BT_HDR *) p_buf;
+
+ if (bt_hc_cbacks)
+ bt_hc_cbacks->dealloc((TRANSAC) p_buf, (char *) (p_hdr+1));
+}
+
+/******************************************************************************
+**
+** Function xmit_cb
+**
+** Description HOST/CONTROLLER VEDNOR LIB CALLOUT API - This function is
+** called from the libbt-vendor in order to send a prepared
+** HCI command packet through HCI transport TX function.
+**
+** Returns TRUE/FALSE
+**
+******************************************************************************/
+static uint8_t xmit_cb(uint16_t opcode, void *p_buf, tINT_CMD_CBACK p_cback)
+{
+ return p_hci_if->send_int_cmd(opcode, (HC_BT_HDR *)p_buf, p_cback);
+}
+
+/*****************************************************************************
+** The libbt-vendor Callback Functions Table
+*****************************************************************************/
+static const bt_vendor_callbacks_t vnd_callbacks = {
+ sizeof(bt_vendor_callbacks_t),
+ fwcfg_cb,
+ scocfg_cb,
+ lpm_vnd_cb,
+ alloc,
+ dealloc,
+ xmit_cb
+};
+
+/******************************************************************************
+**
+** Function init_vnd_if
+**
+** Description Initialize vendor lib interface
+**
+** Returns None
+**
+******************************************************************************/
+void init_vnd_if(unsigned char *local_bdaddr)
+{
+ void *dlhandle;
+
+ dlhandle = dlopen("libbt-vendor.so", RTLD_NOW);
+ if (!dlhandle)
+ {
+ ALOGE("!!! Failed to load libbt-vendor.so !!!");
+ return;
+ }
+
+ bt_vnd_if = (bt_vendor_interface_t *) dlsym(dlhandle, "BLUETOOTH_VENDOR_LIB_INTERFACE");
+ if (!bt_vnd_if)
+ {
+ ALOGE("!!! Failed to get bt vendor interface !!!");
+ return;
+ }
+
+ bt_vnd_if->init(&vnd_callbacks, local_bdaddr);
+}
+
diff --git a/hci/src/btsnoop.c b/hci/src/btsnoop.c
new file mode 100755
index 0000000..4807df8
--- /dev/null
+++ b/hci/src/btsnoop.c
@@ -0,0 +1,694 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2009-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/****************************************************************************
+ *
+ * Name: btsnoopdisp.c
+ *
+ * Function: this file contains functions to generate a BTSNOOP file
+ *
+ *
+ ****************************************************************************/
+#include <stdio.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <pthread.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+/* for gettimeofday */
+#include <sys/time.h>
+/* for the S_* open parameters */
+#include <sys/stat.h>
+/* for write */
+#include <unistd.h>
+/* for O_* open parameters */
+#include <fcntl.h>
+/* defines the O_* open parameters */
+#include <fcntl.h>
+
+#define LOG_TAG "BTSNOOP-DISP"
+#include <cutils/log.h>
+
+#include "bt_hci_bdroid.h"
+#include "utils.h"
+
+#ifndef BTSNOOP_DBG
+#define BTSNOOP_DBG FALSE
+#endif
+
+#if (BTSNOOP_DBG == TRUE)
+#define SNOOPDBG(param, ...) {ALOGD(param, ## __VA_ARGS__);}
+#else
+#define SNOOPDBG(param, ...) {}
+#endif
+
+/* file descriptor of the BT snoop file (by default, -1 means disabled) */
+int hci_btsnoop_fd = -1;
+
+/* Macro to perform a multiplication of 2 unsigned 32bit values and store the result
+ * in an unsigned 64 bit value (as two 32 bit variables):
+ * u64 = u32In1 * u32In2
+ * u32OutLow = u64[31:0]
+ * u32OutHi = u64[63:32]
+ * basically the algorithm:
+ * (hi1*2^16 + lo1)*(hi2*2^16 + lo2) = lo1*lo2 + (hi1*hi2)*2^32 + (hi1*lo2 + hi2*lo1)*2^16
+ * and the carry is propagated 16 bit by 16 bit:
+ * result[15:0] = lo1*lo2 & 0xFFFF
+ * result[31:16] = ((lo1*lo2) >> 16) + (hi1*lo2 + hi2*lo1)
+ * and so on
+ */
+#define HCIDISP_MULT_64(u32In1, u32In2, u32OutLo, u32OutHi) \
+do { \
+ uint32_t u32In1Tmp = u32In1; \
+ uint32_t u32In2Tmp = u32In2; \
+ uint32_t u32Tmp, u32Carry; \
+ u32OutLo = (u32In1Tmp & 0xFFFF) * (u32In2Tmp & 0xFFFF); /*lo1*lo2*/ \
+ u32OutHi = ((u32In1Tmp >> 16) & 0xFFFF) * ((u32In2Tmp >> 16) & 0xFFFF); /*hi1*hi2*/ \
+ u32Tmp = (u32In1Tmp & 0xFFFF) * ((u32In2Tmp >> 16) & 0xFFFF); /*lo1*hi2*/ \
+ u32Carry = (uint32_t)((u32OutLo>>16)&0xFFFF); \
+ u32Carry += (u32Tmp&0xFFFF); \
+ u32OutLo += (u32Tmp << 16) ; \
+ u32OutHi += (u32Tmp >> 16); \
+ u32Tmp = ((u32In1Tmp >> 16) & 0xFFFF) * (u32In2Tmp & 0xFFFF); \
+ u32Carry += (u32Tmp)&0xFFFF; \
+ u32Carry>>=16; \
+ u32OutLo += (u32Tmp << 16); \
+ u32OutHi += (u32Tmp >> 16); \
+ u32OutHi += u32Carry; \
+} while (0)
+
+/* Macro to make an addition of 2 64 bit values:
+ * result = (u32OutHi & u32OutLo) + (u32InHi & u32InLo)
+ * u32OutHi = result[63:32]
+ * u32OutLo = result[31:0]
+ */
+#define HCIDISP_ADD_64(u32InLo, u32InHi, u32OutLo, u32OutHi) \
+do { \
+ (u32OutLo) += (u32InLo); \
+ if ((u32OutLo) < (u32InLo)) (u32OutHi)++; \
+ (u32OutHi) += (u32InHi); \
+} while (0)
+
+/* EPOCH in microseconds since 01/01/0000 : 0x00dcddb3.0f2f8000 */
+#define BTSNOOP_EPOCH_HI 0x00dcddb3U
+#define BTSNOOP_EPOCH_LO 0x0f2f8000U
+
+/*******************************************************************************
+ **
+ ** Function tv_to_btsnoop_ts
+ **
+ ** Description This function generate a BT Snoop timestamp.
+ **
+ ** Returns void
+ **
+ ** NOTE
+ ** The return value is 64 bit as 2 32 bit variables out_lo and * out_hi.
+ ** A BT Snoop timestamp is the number of microseconds since 01/01/0000.
+ ** The timeval structure contains the number of microseconds since EPOCH
+ ** (01/01/1970) encoded as: tv.tv_sec, number of seconds since EPOCH and
+ ** tv_usec, number of microseconds in current second
+ **
+ ** Therefore the algorithm is:
+ ** result = tv.tv_sec * 1000000
+ ** result += tv.tv_usec
+ ** result += EPOCH_OFFSET
+ *******************************************************************************/
+static void tv_to_btsnoop_ts(uint32_t *out_lo, uint32_t *out_hi, struct timeval *tv)
+{
+ /* multiply the seconds by 1000000 */
+ HCIDISP_MULT_64(tv->tv_sec, 0xf4240, *out_lo, *out_hi);
+
+ /* add the microseconds */
+ HCIDISP_ADD_64((uint32_t)tv->tv_usec, 0, *out_lo, *out_hi);
+
+ /* add the epoch */
+ HCIDISP_ADD_64(BTSNOOP_EPOCH_LO, BTSNOOP_EPOCH_HI, *out_lo, *out_hi);
+}
+
+/*******************************************************************************
+ **
+ ** Function l_to_be
+ **
+ ** Description Function to convert a 32 bit value into big endian format
+ **
+ ** Returns 32 bit value in big endian format
+*******************************************************************************/
+static uint32_t l_to_be(uint32_t x)
+{
+ #if __BIG_ENDIAN != TRUE
+ x = (x >> 24) |
+ ((x >> 8) & 0xFF00) |
+ ((x << 8) & 0xFF0000) |
+ (x << 24);
+ #endif
+ return x;
+}
+
+/*******************************************************************************
+ **
+ ** Function btsnoop_is_open
+ **
+ ** Description Function to check if BTSNOOP is open
+ **
+ ** Returns 1 if open otherwise 0
+*******************************************************************************/
+int btsnoop_is_open(void)
+{
+#if defined(BTSNOOPDISP_INCLUDED) && (BTSNOOPDISP_INCLUDED == TRUE)
+ SNOOPDBG("btsnoop_is_open: snoop fd = %d\n", hci_btsnoop_fd);
+
+ if (hci_btsnoop_fd != -1)
+ {
+ return 1;
+ }
+ return 0;
+#else
+ return 2; /* Snoop not available */
+#endif
+}
+
+/*******************************************************************************
+ **
+ ** Function btsnoop_log_open
+ **
+ ** Description Function to open the BTSNOOP file
+ **
+ ** Returns None
+*******************************************************************************/
+static int btsnoop_log_open(char *btsnoop_logfile)
+{
+#if defined(BTSNOOPDISP_INCLUDED) && (BTSNOOPDISP_INCLUDED == TRUE)
+ hci_btsnoop_fd = -1;
+
+ SNOOPDBG("btsnoop_log_open: snoop log file = %s\n", btsnoop_logfile);
+
+ /* write the BT snoop header */
+ if ((btsnoop_logfile != NULL) && (strlen(btsnoop_logfile) != 0))
+ {
+ hci_btsnoop_fd = open(btsnoop_logfile, \
+ O_WRONLY|O_CREAT|O_TRUNC, \
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
+ if (hci_btsnoop_fd == -1)
+ {
+ perror("open");
+ SNOOPDBG("btsnoop_log_open: Unable to open snoop log file\n");
+ hci_btsnoop_fd = -1;
+ return 0;
+ }
+ write(hci_btsnoop_fd, "btsnoop\0\0\0\0\1\0\0\x3\xea", 16);
+ return 1;
+ }
+#endif
+ return 2; /* Snoop not available */
+}
+
+/*******************************************************************************
+ **
+ ** Function btsnoop_log_close
+ **
+ ** Description Function to close the BTSNOOP file
+ **
+ ** Returns None
+*******************************************************************************/
+static int btsnoop_log_close(void)
+{
+#if defined(BTSNOOPDISP_INCLUDED) && (BTSNOOPDISP_INCLUDED == TRUE)
+ /* write the BT snoop header */
+ if (hci_btsnoop_fd != -1)
+ {
+ SNOOPDBG("btsnoop_log_close: Closing snoop log file\n");
+ close(hci_btsnoop_fd);
+ hci_btsnoop_fd = -1;
+ return 1;
+ }
+ return 0;
+#else
+ return 2; /* Snoop not available */
+#endif
+}
+
+/*******************************************************************************
+ **
+ ** Function btsnoop_hci_cmd
+ **
+ ** Description Function to add a command in the BTSNOOP file
+ **
+ ** Returns None
+*******************************************************************************/
+void btsnoop_hci_cmd(uint8_t *p)
+{
+ SNOOPDBG("btsnoop_hci_cmd: fd = %d", hci_btsnoop_fd);
+
+ if (hci_btsnoop_fd != -1)
+ {
+ uint32_t value, value_hi;
+ struct timeval tv;
+
+ /* since these display functions are called from different contexts */
+ utils_lock();
+
+ /* store the length in both original and included fields */
+ value = l_to_be(p[2] + 4);
+ write(hci_btsnoop_fd, &value, 4);
+ write(hci_btsnoop_fd, &value, 4);
+ /* flags: command sent from the host */
+ value = l_to_be(2);
+ write(hci_btsnoop_fd, &value, 4);
+ /* drops: none */
+ value = 0;
+ write(hci_btsnoop_fd, &value, 4);
+ /* time */
+ gettimeofday(&tv, NULL);
+ tv_to_btsnoop_ts(&value, &value_hi, &tv);
+ value_hi = l_to_be(value_hi);
+ value = l_to_be(value);
+ write(hci_btsnoop_fd, &value_hi, 4);
+ write(hci_btsnoop_fd, &value, 4);
+ /* data */
+ write(hci_btsnoop_fd, "\x1", 1);
+ write(hci_btsnoop_fd, p, p[2] + 3);
+
+ /* since these display functions are called from different contexts */
+ utils_unlock();
+ }
+}
+
+/*******************************************************************************
+ **
+ ** Function btsnoop_hci_evt
+ **
+ ** Description Function to add a event in the BTSNOOP file
+ **
+ ** Returns None
+*******************************************************************************/
+void btsnoop_hci_evt(uint8_t *p)
+{
+ SNOOPDBG("btsnoop_hci_evt: fd = %d", hci_btsnoop_fd);
+
+ if (hci_btsnoop_fd != -1)
+ {
+ uint32_t value, value_hi;
+ struct timeval tv;
+
+ /* since these display functions are called from different contexts */
+ utils_lock();
+
+ /* store the length in both original and included fields */
+ value = l_to_be(p[1] + 3);
+ write(hci_btsnoop_fd, &value, 4);
+ write(hci_btsnoop_fd, &value, 4);
+ /* flags: event received in the host */
+ value = l_to_be(3);
+ write(hci_btsnoop_fd, &value, 4);
+ /* drops: none */
+ value = 0;
+ write(hci_btsnoop_fd, &value, 4);
+ /* time */
+ gettimeofday(&tv, NULL);
+ tv_to_btsnoop_ts(&value, &value_hi, &tv);
+ value_hi = l_to_be(value_hi);
+ value = l_to_be(value);
+ write(hci_btsnoop_fd, &value_hi, 4);
+ write(hci_btsnoop_fd, &value, 4);
+ /* data */
+ write(hci_btsnoop_fd, "\x4", 1);
+ write(hci_btsnoop_fd, p, p[1] + 2);
+
+ /* since these display functions are called from different contexts */
+ utils_unlock();
+ }
+}
+
+/*******************************************************************************
+ **
+ ** Function btsnoop_sco_data
+ **
+ ** Description Function to add a SCO data packet in the BTSNOOP file
+ **
+ ** Returns None
+*******************************************************************************/
+void btsnoop_sco_data(uint8_t *p, uint8_t is_rcvd)
+{
+ SNOOPDBG("btsnoop_sco_data: fd = %d", hci_btsnoop_fd);
+
+ if (hci_btsnoop_fd != -1)
+ {
+ uint32_t value, value_hi;
+ struct timeval tv;
+
+ /* since these display functions are called from different contexts */
+ utils_lock();
+
+ /* store the length in both original and included fields */
+ value = l_to_be(p[2] + 4);
+ write(hci_btsnoop_fd, &value, 4);
+ write(hci_btsnoop_fd, &value, 4);
+ /* flags: data can be sent or received */
+ value = l_to_be(is_rcvd?1:0);
+ write(hci_btsnoop_fd, &value, 4);
+ /* drops: none */
+ value = 0;
+ write(hci_btsnoop_fd, &value, 4);
+ /* time */
+ gettimeofday(&tv, NULL);
+ tv_to_btsnoop_ts(&value, &value_hi, &tv);
+ value_hi = l_to_be(value_hi);
+ value = l_to_be(value);
+ write(hci_btsnoop_fd, &value_hi, 4);
+ write(hci_btsnoop_fd, &value, 4);
+ /* data */
+ write(hci_btsnoop_fd, "\x3", 1);
+ write(hci_btsnoop_fd, p, p[2] + 3);
+
+ /* since these display functions are called from different contexts */
+ utils_unlock();
+ }
+}
+
+/*******************************************************************************
+ **
+ ** Function btsnoop_acl_data
+ **
+ ** Description Function to add an ACL data packet in the BTSNOOP file
+ **
+ ** Returns None
+*******************************************************************************/
+void btsnoop_acl_data(uint8_t *p, uint8_t is_rcvd)
+{
+ SNOOPDBG("btsnoop_acl_data: fd = %d", hci_btsnoop_fd);
+ if (hci_btsnoop_fd != -1)
+ {
+ uint32_t value, value_hi;
+ struct timeval tv;
+
+ /* since these display functions are called from different contexts */
+ utils_lock();
+
+ /* store the length in both original and included fields */
+ value = l_to_be((p[3]<<8) + p[2] + 5);
+ write(hci_btsnoop_fd, &value, 4);
+ write(hci_btsnoop_fd, &value, 4);
+ /* flags: data can be sent or received */
+ value = l_to_be(is_rcvd?1:0);
+ write(hci_btsnoop_fd, &value, 4);
+ /* drops: none */
+ value = 0;
+ write(hci_btsnoop_fd, &value, 4);
+ /* time */
+ gettimeofday(&tv, NULL);
+ tv_to_btsnoop_ts(&value, &value_hi, &tv);
+ value_hi = l_to_be(value_hi);
+ value = l_to_be(value);
+ write(hci_btsnoop_fd, &value_hi, 4);
+ write(hci_btsnoop_fd, &value, 4);
+ /* data */
+ write(hci_btsnoop_fd, "\x2", 1);
+ write(hci_btsnoop_fd, p, (p[3]<<8) + p[2] + 4);
+
+ /* since these display functions are called from different contexts */
+ utils_unlock();
+ }
+}
+
+
+/********************************************************************************
+ ** API allow external realtime parsing of output using e.g hcidump
+ *********************************************************************************/
+
+#define EXT_PARSER_PORT 4330
+
+static pthread_t thread_id;
+static int s_listen = -1;
+static int ext_parser_fd = -1;
+
+static void ext_parser_detached(void);
+
+static int ext_parser_accept(int port)
+{
+ socklen_t clilen;
+ struct sockaddr_in cliaddr, servaddr;
+ int s, srvlen;
+ int n = 1;
+ int size_n;
+ int result = 0;
+
+ ALOGD("waiting for connection on port %d", port);
+
+ s_listen = socket(AF_INET, SOCK_STREAM, 0);
+
+ if (s_listen < 0)
+ {
+ ALOGE("listener not created: listen fd %d", s_listen);
+ return -1;
+ }
+
+ bzero(&servaddr, sizeof(servaddr));
+ servaddr.sin_family = AF_INET;
+ servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ servaddr.sin_port = htons(port);
+
+ srvlen = sizeof(servaddr);
+
+ /* allow reuse of sock addr upon bind */
+ result = setsockopt(s_listen, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
+
+ if (result<0)
+ {
+ perror("setsockopt");
+ }
+
+ result = bind(s_listen, (struct sockaddr *) &servaddr, srvlen);
+
+ if (result < 0)
+ perror("bind");
+
+ result = listen(s_listen, 1);
+
+ if (result < 0)
+ perror("listen");
+
+ clilen = sizeof(struct sockaddr_in);
+
+ s = accept(s_listen, (struct sockaddr *) &cliaddr, &clilen);
+
+ if (s < 0)
+{
+ perror("accept");
+ return -1;
+ }
+
+ ALOGD("connected (%d)", s);
+
+ return s;
+}
+
+static int send_ext_parser(char *p, int len)
+{
+ int n;
+
+ /* check if io socket is connected */
+ if (ext_parser_fd == -1)
+ return 0;
+
+ SNOOPDBG("write %d to snoop socket\n", len);
+
+ n = write(ext_parser_fd, p, len);
+
+ if (n<=0)
+ {
+ ext_parser_detached();
+ }
+
+ return n;
+}
+
+static void ext_parser_detached(void)
+{
+ ALOGD("ext parser detached");
+
+ if (ext_parser_fd>0)
+ close(ext_parser_fd);
+
+ if (s_listen > 0)
+ close(s_listen);
+
+ ext_parser_fd = -1;
+ s_listen = -1;
+}
+
+static void interruptFn (int sig)
+{
+ ALOGD("interruptFn");
+ pthread_exit(0);
+}
+
+static void ext_parser_thread(void* param)
+{
+ int fd;
+ int sig = SIGUSR2;
+ sigset_t sigSet;
+ sigemptyset (&sigSet);
+ sigaddset (&sigSet, sig);
+
+ ALOGD("ext_parser_thread");
+
+ prctl(PR_SET_NAME, (unsigned long)"BtsnoopExtParser", 0, 0, 0);
+
+ pthread_sigmask (SIG_UNBLOCK, &sigSet, NULL);
+
+ struct sigaction act;
+ act.sa_handler = interruptFn;
+ sigaction (sig, &act, NULL );
+
+ do
+ {
+ fd = ext_parser_accept(EXT_PARSER_PORT);
+
+ ext_parser_fd = fd;
+
+ ALOGD("ext parser attached on fd %d\n", ext_parser_fd);
+ } while (1);
+}
+
+void btsnoop_stop_listener(void)
+{
+ ALOGD("btsnoop_init");
+ ext_parser_detached();
+}
+
+void btsnoop_init(void)
+{
+#if defined(BTSNOOP_EXT_PARSER_INCLUDED) && (BTSNOOP_EXT_PARSER_INCLUDED == TRUE)
+ ALOGD("btsnoop_init");
+
+ /* always setup ext listener port */
+ if (pthread_create(&thread_id, NULL,
+ (void*)ext_parser_thread,NULL)!=0)
+ perror("pthread_create");
+#endif
+}
+
+void btsnoop_open(char *p_path)
+{
+#if defined(BTSNOOPDISP_INCLUDED) && (BTSNOOPDISP_INCLUDED == TRUE)
+ ALOGD("btsnoop_open");
+ btsnoop_log_open(p_path);
+#endif // BTSNOOPDISP_INCLUDED
+}
+
+void btsnoop_close(void)
+{
+#if defined(BTSNOOPDISP_INCLUDED) && (BTSNOOPDISP_INCLUDED == TRUE)
+ ALOGD("btsnoop_close");
+ btsnoop_log_close();
+#endif
+}
+
+void btsnoop_cleanup (void)
+{
+#if defined(BTSNOOP_EXT_PARSER_INCLUDED) && (BTSNOOP_EXT_PARSER_INCLUDED == TRUE)
+ ALOGD("btsnoop_cleanup");
+ pthread_kill(thread_id, SIGUSR2);
+ pthread_join(thread_id, NULL);
+ ext_parser_detached();
+#endif
+}
+
+
+#define HCIT_TYPE_COMMAND 1
+#define HCIT_TYPE_ACL_DATA 2
+#define HCIT_TYPE_SCO_DATA 3
+#define HCIT_TYPE_EVENT 4
+
+void btsnoop_capture(HC_BT_HDR *p_buf, uint8_t is_rcvd)
+{
+ uint8_t *p = (uint8_t *)(p_buf + 1) + p_buf->offset;
+
+ SNOOPDBG("btsnoop_capture: fd = %d, type %x, rcvd %d, ext %d", \
+ hci_btsnoop_fd, p_buf->event, is_rcvd, ext_parser_fd);
+
+#if defined(BTSNOOP_EXT_PARSER_INCLUDED) && (BTSNOOP_EXT_PARSER_INCLUDED == TRUE)
+ if (ext_parser_fd > 0)
+ {
+ uint8_t tmp = *p;
+
+ /* borrow one byte for H4 packet type indicator */
+ p--;
+
+ switch (p_buf->event & MSG_EVT_MASK)
+ {
+ case MSG_HC_TO_STACK_HCI_EVT:
+ *p = HCIT_TYPE_EVENT;
+ break;
+ case MSG_HC_TO_STACK_HCI_ACL:
+ case MSG_STACK_TO_HC_HCI_ACL:
+ *p = HCIT_TYPE_ACL_DATA;
+ break;
+ case MSG_HC_TO_STACK_HCI_SCO:
+ case MSG_STACK_TO_HC_HCI_SCO:
+ *p = HCIT_TYPE_SCO_DATA;
+ break;
+ case MSG_STACK_TO_HC_HCI_CMD:
+ *p = HCIT_TYPE_COMMAND;
+ break;
+ }
+
+ send_ext_parser((char*)p, p_buf->len+1);
+ *(++p) = tmp;
+ return;
+ }
+#endif
+
+#if defined(BTSNOOPDISP_INCLUDED) && (BTSNOOPDISP_INCLUDED == TRUE)
+ if (hci_btsnoop_fd == -1)
+ return;
+
+ switch (p_buf->event & MSG_EVT_MASK)
+ {
+ case MSG_HC_TO_STACK_HCI_EVT:
+ SNOOPDBG("TYPE : EVT");
+ btsnoop_hci_evt(p);
+ break;
+ case MSG_HC_TO_STACK_HCI_ACL:
+ case MSG_STACK_TO_HC_HCI_ACL:
+ SNOOPDBG("TYPE : ACL");
+ btsnoop_acl_data(p, is_rcvd);
+ break;
+ case MSG_HC_TO_STACK_HCI_SCO:
+ case MSG_STACK_TO_HC_HCI_SCO:
+ SNOOPDBG("TYPE : SCO");
+ btsnoop_sco_data(p, is_rcvd);
+ break;
+ case MSG_STACK_TO_HC_HCI_CMD:
+ SNOOPDBG("TYPE : CMD");
+ btsnoop_hci_cmd(p);
+ break;
+ }
+#endif // BTSNOOPDISP_INCLUDED
+}
+
+
diff --git a/hci/src/hci_h4.c b/hci/src/hci_h4.c
new file mode 100644
index 0000000..ba15426
--- /dev/null
+++ b/hci/src/hci_h4.c
@@ -0,0 +1,1075 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2009-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * Filename: hci_h4.c
+ *
+ * Description: Contains HCI transport send/receive functions
+ *
+ ******************************************************************************/
+
+#define LOG_TAG "bt_h4"
+
+#include <utils/Log.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include "bt_hci_bdroid.h"
+#include "hci.h"
+#include "userial.h"
+#include "utils.h"
+
+/******************************************************************************
+** Constants & Macros
+******************************************************************************/
+
+#ifndef HCI_DBG
+#define HCI_DBG FALSE
+#endif
+
+#if (HCI_DBG == TRUE)
+#define HCIDBG(param, ...) {LOGD(param, ## __VA_ARGS__);}
+#else
+#define HCIDBG(param, ...) {}
+#endif
+
+/* Preamble length for HCI Commands:
+** 2-bytes for opcode and 1 byte for length
+*/
+#define HCI_CMD_PREAMBLE_SIZE 3
+
+/* Preamble length for HCI Events:
+** 1-byte for opcode and 1 byte for length
+*/
+#define HCI_EVT_PREAMBLE_SIZE 2
+
+/* Preamble length for SCO Data:
+** 2-byte for Handle and 1 byte for length
+*/
+#define HCI_SCO_PREAMBLE_SIZE 3
+
+/* Preamble length for ACL Data:
+** 2-byte for Handle and 2 byte for length
+*/
+#define HCI_ACL_PREAMBLE_SIZE 4
+
+/* Table of HCI preamble sizes for the different HCI message types */
+static const uint8_t hci_preamble_table[] =
+{
+ HCI_CMD_PREAMBLE_SIZE,
+ HCI_ACL_PREAMBLE_SIZE,
+ HCI_SCO_PREAMBLE_SIZE,
+ HCI_EVT_PREAMBLE_SIZE
+};
+
+/* HCI H4 message type definitions */
+#define H4_TYPE_COMMAND 1
+#define H4_TYPE_ACL_DATA 2
+#define H4_TYPE_SCO_DATA 3
+#define H4_TYPE_EVENT 4
+
+static const uint16_t msg_evt_table[] =
+{
+ MSG_HC_TO_STACK_HCI_ERR, /* H4_TYPE_COMMAND */
+ MSG_HC_TO_STACK_HCI_ACL, /* H4_TYPE_ACL_DATA */
+ MSG_HC_TO_STACK_HCI_SCO, /* H4_TYPE_SCO_DATA */
+ MSG_HC_TO_STACK_HCI_EVT /* H4_TYPE_EVENT */
+};
+
+#define ACL_RX_PKT_START 2
+#define ACL_RX_PKT_CONTINUE 1
+#define L2CAP_HEADER_SIZE 4
+
+/* Maximum numbers of allowed internal
+** outstanding command packets at any time
+*/
+#define INT_CMD_PKT_MAX_COUNT 8
+#define INT_CMD_PKT_IDX_MASK 0x07
+
+#define HCI_COMMAND_COMPLETE_EVT 0x0E
+#define HCI_COMMAND_STATUS_EVT 0x0F
+#define HCI_READ_BUFFER_SIZE 0x1005
+#define HCI_LE_READ_BUFFER_SIZE 0x2002
+
+/******************************************************************************
+** Local type definitions
+******************************************************************************/
+
+/* H4 Rx States */
+typedef enum {
+ H4_RX_MSGTYPE_ST,
+ H4_RX_LEN_ST,
+ H4_RX_DATA_ST,
+ H4_RX_IGNORE_ST
+} tHCI_H4_RCV_STATE;
+
+/* Callback function for the returned event of internal issued command */
+typedef void (*tINT_CMD_CBACK)(void *p_mem);
+
+typedef struct
+{
+ uint16_t opcode; /* OPCODE of outstanding internal commands */
+ tINT_CMD_CBACK cback; /* Callback function when return of internal
+ * command is received */
+} tINT_CMD_Q;
+
+/* Control block for HCISU_H4 */
+typedef struct
+{
+ HC_BT_HDR *p_rcv_msg; /* Buffer to hold current rx HCI message */
+ uint16_t rcv_len; /* Size of current incoming message */
+ uint8_t rcv_msg_type; /* Current incoming message type */
+ tHCI_H4_RCV_STATE rcv_state; /* Receive state of current rx message */
+ uint16_t hc_acl_data_size; /* Controller's max ACL data length */
+ uint16_t hc_ble_acl_data_size; /* Controller's max BLE ACL data length */
+ BUFFER_Q acl_rx_q; /* Queue of base buffers for fragmented ACL pkts */
+ uint8_t preload_count; /* Count numbers of preload bytes */
+ uint8_t preload_buffer[6]; /* HCI_ACL_PREAMBLE_SIZE + 2 */
+ int int_cmd_rsp_pending; /* Num of internal cmds pending for ack */
+ uint8_t int_cmd_rd_idx; /* Read index of int_cmd_opcode queue */
+ uint8_t int_cmd_wrt_idx; /* Write index of int_cmd_opcode queue */
+ tINT_CMD_Q int_cmd[INT_CMD_PKT_MAX_COUNT]; /* FIFO queue */
+} tHCI_H4_CB;
+
+/******************************************************************************
+** Externs
+******************************************************************************/
+
+extern BUFFER_Q tx_q;
+
+void btsnoop_init(void);
+void btsnoop_close(void);
+void btsnoop_cleanup (void);
+void btsnoop_capture(HC_BT_HDR *p_buf, uint8_t is_rcvd);
+uint8_t hci_h4_send_int_cmd(uint16_t opcode, HC_BT_HDR *p_buf, \
+ tINT_CMD_CBACK p_cback);
+void lpm_wake_assert(void);
+void lpm_tx_done(uint8_t is_tx_done);
+
+/******************************************************************************
+** Variables
+******************************************************************************/
+
+/* Num of allowed outstanding HCI CMD packets */
+volatile int num_hci_cmd_pkts = 1;
+
+/******************************************************************************
+** Static variables
+******************************************************************************/
+
+static tHCI_H4_CB h4_cb;
+
+/******************************************************************************
+** Static functions
+******************************************************************************/
+
+/*******************************************************************************
+**
+** Function get_acl_data_length_cback
+**
+** Description Callback function for HCI_READ_BUFFER_SIZE and
+** HCI_LE_READ_BUFFER_SIZE commands if they were sent because
+** of internal request.
+**
+** Returns None
+**
+*******************************************************************************/
+void get_acl_data_length_cback(void *p_mem)
+{
+ uint8_t *p, status;
+ uint16_t opcode, len=0;
+ HC_BT_HDR *p_buf = (HC_BT_HDR *) p_mem;
+
+ p = (uint8_t *)(p_buf + 1) + 3;
+ STREAM_TO_UINT16(opcode, p)
+ status = *p++;
+ if (status == 0) /* Success */
+ STREAM_TO_UINT16(len, p)
+
+ if (opcode == HCI_READ_BUFFER_SIZE)
+ {
+ if (status == 0)
+ h4_cb.hc_acl_data_size = len;
+
+ /* reuse the rx buffer for sending HCI_LE_READ_BUFFER_SIZE command */
+ p_buf->event = MSG_STACK_TO_HC_HCI_CMD;
+ p_buf->offset = 0;
+ p_buf->layer_specific = 0;
+ p_buf->len = 3;
+
+ p = (uint8_t *) (p_buf + 1);
+ UINT16_TO_STREAM(p, HCI_LE_READ_BUFFER_SIZE);
+ *p = 0;
+
+ if ((status = hci_h4_send_int_cmd(HCI_LE_READ_BUFFER_SIZE, p_buf, \
+ get_acl_data_length_cback)) == FALSE)
+ {
+ bt_hc_cbacks->dealloc((TRANSAC) p_buf, (char *) (p_buf + 1));
+ bt_hc_cbacks->postload_cb(NULL, BT_HC_POSTLOAD_SUCCESS);
+ }
+ }
+ else if (opcode == HCI_LE_READ_BUFFER_SIZE)
+ {
+ if (status == 0)
+ h4_cb.hc_ble_acl_data_size = (len) ? len : h4_cb.hc_acl_data_size;
+
+ if (bt_hc_cbacks)
+ {
+ bt_hc_cbacks->dealloc((TRANSAC) p_buf, (char *) (p_buf + 1));
+ ALOGE("vendor lib postload completed");
+ bt_hc_cbacks->postload_cb(NULL, BT_HC_POSTLOAD_SUCCESS);
+ }
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function internal_event_intercept
+**
+** Description This function is called to parse received HCI event and
+** - update the Num_HCI_Command_Packets
+** - intercept the event if it is the result of an early
+** issued internal command.
+**
+** Returns TRUE : if the event had been intercepted for internal process
+** FALSE : send this event to core stack
+**
+*******************************************************************************/
+uint8_t internal_event_intercept(void)
+{
+ uint8_t *p;
+ uint8_t event_code;
+ uint16_t opcode, len;
+ tHCI_H4_CB *p_cb = &h4_cb;
+
+ p = (uint8_t *)(p_cb->p_rcv_msg + 1);
+
+ event_code = *p++;
+ len = *p++;
+
+ if (event_code == HCI_COMMAND_COMPLETE_EVT)
+ {
+ num_hci_cmd_pkts = *p++;
+
+ if (p_cb->int_cmd_rsp_pending > 0)
+ {
+ STREAM_TO_UINT16(opcode, p)
+
+ if (opcode == p_cb->int_cmd[p_cb->int_cmd_rd_idx].opcode)
+ {
+ HCIDBG( \
+ "Intercept CommandCompleteEvent for internal command (0x%04X)",\
+ opcode);
+ if (p_cb->int_cmd[p_cb->int_cmd_rd_idx].cback != NULL)
+ {
+ p_cb->int_cmd[p_cb->int_cmd_rd_idx].cback(p_cb->p_rcv_msg);
+ }
+ else
+ {
+ // Missing cback function!
+ // Release the p_rcv_msg buffer.
+ if (bt_hc_cbacks)
+ {
+ bt_hc_cbacks->dealloc((TRANSAC) p_cb->p_rcv_msg, \
+ (char *) (p_cb->p_rcv_msg + 1));
+ }
+ }
+ p_cb->int_cmd_rd_idx = ((p_cb->int_cmd_rd_idx+1) & \
+ INT_CMD_PKT_IDX_MASK);
+ p_cb->int_cmd_rsp_pending--;
+ return TRUE;
+ }
+ }
+ }
+ else if (event_code == HCI_COMMAND_STATUS_EVT)
+ {
+ num_hci_cmd_pkts = *(++p);
+ }
+
+ return FALSE;
+}
+
+/*******************************************************************************
+**
+** Function acl_rx_frame_buffer_alloc
+**
+** Description This function is called from the HCI transport when the
+** first 4 or 6 bytes of an HCI ACL packet have been received:
+** - Allocate a new buffer if it is a start pakcet of L2CAP
+** message.
+** - Return the buffer address of the starting L2CAP message
+** frame if the packet is the next segment of a fragmented
+** L2CAP message.
+**
+** Returns the address of the receive buffer H4 RX should use
+** (CR419: Modified to return NULL in case of error.)
+**
+** NOTE This assumes that the L2CAP MTU size is less than the size
+** of an HCI ACL buffer, so the maximum L2CAP message will fit
+** into one buffer.
+**
+*******************************************************************************/
+static HC_BT_HDR *acl_rx_frame_buffer_alloc (void)
+{
+ uint8_t *p;
+ uint16_t handle;
+ uint16_t hci_len;
+ uint16_t total_len;
+ uint8_t pkt_type;
+ HC_BT_HDR *p_return_buf = NULL;
+ tHCI_H4_CB *p_cb = &h4_cb;
+
+
+ p = p_cb->preload_buffer;
+
+ STREAM_TO_UINT16 (handle, p);
+ STREAM_TO_UINT16 (hci_len, p);
+ STREAM_TO_UINT16 (total_len, p);
+
+ pkt_type = (uint8_t)(((handle) >> 12) & 0x0003);
+ handle = (uint16_t)((handle) & 0x0FFF);
+
+ if (p_cb->acl_rx_q.count)
+ {
+ uint16_t save_handle;
+ HC_BT_HDR *p_hdr = p_cb->acl_rx_q.p_first;
+
+ while (p_hdr != NULL)
+ {
+ p = (uint8_t *)(p_hdr + 1);
+ STREAM_TO_UINT16 (save_handle, p);
+ save_handle = (uint16_t)((save_handle) & 0x0FFF);
+ if (save_handle == handle)
+ {
+ p_return_buf = p_hdr;
+ break;
+ }
+ p_hdr = utils_getnext(p_hdr);
+ }
+ }
+
+ if (pkt_type == ACL_RX_PKT_START) /*** START PACKET ***/
+ {
+ /* Might have read 2 bytes for the L2CAP payload length */
+ p_cb->rcv_len = (hci_len) ? (hci_len - 2) : 0;
+
+ /* Start of packet. If we were in the middle of receiving */
+ /* a packet on the same ACL handle, the original packet is incomplete.
+ * Drop it. */
+ if (p_return_buf)
+ {
+ ALOGW("H4 - dropping incomplete ACL frame");
+
+ utils_remove_from_queue(&(p_cb->acl_rx_q), p_return_buf);
+
+ if (bt_hc_cbacks)
+ {
+ bt_hc_cbacks->dealloc((TRANSAC) p_return_buf, \
+ (char *) (p_return_buf + 1));
+ }
+ p_return_buf = NULL;
+ }
+
+ /* Allocate a buffer for message */
+ if (bt_hc_cbacks)
+ {
+ int len = total_len + HCI_ACL_PREAMBLE_SIZE + L2CAP_HEADER_SIZE + \
+ BT_HC_HDR_SIZE;
+ p_return_buf = (HC_BT_HDR *) bt_hc_cbacks->alloc(len);
+ }
+
+ if (p_return_buf)
+ {
+ /* Initialize buffer with preloaded data */
+ p_return_buf->offset = 0;
+ p_return_buf->layer_specific = 0;
+ p_return_buf->event = MSG_HC_TO_STACK_HCI_ACL;
+ p_return_buf->len = p_cb->preload_count;
+ memcpy((uint8_t *)(p_return_buf + 1), p_cb->preload_buffer, \
+ p_cb->preload_count);
+
+ if (hci_len && ((total_len + L2CAP_HEADER_SIZE) > hci_len))
+ {
+ /* Will expect to see fragmented ACL packets */
+ /* Keep the base buffer address in the watching queue */
+ utils_enqueue(&(p_cb->acl_rx_q), p_return_buf);
+ }
+ }
+ }
+ else /*** CONTINUATION PACKET ***/
+ {
+ p_cb->rcv_len = hci_len;
+
+ if (p_return_buf)
+ {
+ /* Packet continuation and found the original rx buffer */
+ uint8_t *p_f = p = (uint8_t *)(p_return_buf + 1) + 2;
+
+ STREAM_TO_UINT16 (total_len, p);
+
+ /* Update HCI header of first segment (base buffer) with new len */
+ total_len += hci_len;
+ UINT16_TO_STREAM (p_f, total_len);
+ }
+ }
+
+ return (p_return_buf);
+}
+
+/*******************************************************************************
+**
+** Function acl_rx_frame_end_chk
+**
+** Description This function is called from the HCI transport when the last
+** byte of an HCI ACL packet has been received. It checks if
+** the L2CAP message is complete, i.e. no more continuation
+** packets are expected.
+**
+** Returns TRUE if message complete, FALSE if continuation expected
+**
+*******************************************************************************/
+static uint8_t acl_rx_frame_end_chk (void)
+{
+ uint8_t *p;
+ uint16_t handle, hci_len, l2cap_len;
+ HC_BT_HDR *p_buf;
+ tHCI_H4_CB *p_cb = &h4_cb;
+ uint8_t frame_end=TRUE;
+
+ p_buf = p_cb->p_rcv_msg;
+ p = (uint8_t *)(p_buf + 1);
+
+ STREAM_TO_UINT16 (handle, p);
+ STREAM_TO_UINT16 (hci_len, p);
+ STREAM_TO_UINT16 (l2cap_len, p);
+
+ if (hci_len > 0)
+ {
+ if (l2cap_len > (p_buf->len-(HCI_ACL_PREAMBLE_SIZE+L2CAP_HEADER_SIZE)) )
+ {
+ /* If the L2CAP length has not been reached, tell H4 not to send
+ * this buffer to stack */
+ frame_end = FALSE;
+ }
+ else
+ {
+ /*
+ * The current buffer coulb be in the watching list.
+ * Remove it from the list if it is in.
+ */
+ if (p_cb->acl_rx_q.count)
+ utils_remove_from_queue(&(p_cb->acl_rx_q), p_buf);
+ }
+ }
+
+ /****
+ ** Print snoop trace
+ ****/
+ if (p_buf->offset)
+ {
+ /* CONTINUATION PACKET */
+
+ /* save original p_buf->len content */
+ uint16_t tmp_u16 = p_buf->len;
+
+ /* borrow HCI_ACL_PREAMBLE_SIZE bytes from the payload section */
+ p = (uint8_t *)(p_buf + 1) + p_buf->offset - HCI_ACL_PREAMBLE_SIZE;
+
+ /* save contents */
+ memcpy(p_cb->preload_buffer, p, HCI_ACL_PREAMBLE_SIZE);
+
+ /* Set packet boundary flags to "continuation packet" */
+ handle = (handle & 0xCFFF) | 0x1000;
+
+ /* write handl & length info */
+ UINT16_TO_STREAM (p, handle);
+ UINT16_TO_STREAM (p, (p_buf->len - p_buf->offset));
+
+ /* roll pointer back */
+ p = p - HCI_ACL_PREAMBLE_SIZE;
+
+ /* adjust `p_buf->offset` & `p_buf->len`
+ * before calling btsnoop_capture() */
+ p_buf->offset = p_buf->offset - HCI_ACL_PREAMBLE_SIZE;
+ p_buf->len = p_buf->len - p_buf->offset;
+
+ btsnoop_capture(p_buf, TRUE);
+
+ /* restore contents */
+ memcpy(p, p_cb->preload_buffer, HCI_ACL_PREAMBLE_SIZE);
+
+ /* restore p_buf->len */
+ p_buf->len = tmp_u16;
+ }
+ else
+ {
+ /* START PACKET */
+ btsnoop_capture(p_buf, TRUE);
+ }
+
+ if (frame_end == TRUE)
+ p_buf->offset = 0;
+ else
+ p_buf->offset = p_buf->len; /* save current buffer-end position */
+
+ return frame_end;
+}
+
+/*****************************************************************************
+** HCI H4 INTERFACE FUNCTIONS
+*****************************************************************************/
+
+/*******************************************************************************
+**
+** Function hci_h4_init
+**
+** Description Initialize H4 module
+**
+** Returns None
+**
+*******************************************************************************/
+void hci_h4_init(void)
+{
+ HCIDBG("hci_h4_init");
+
+ memset(&h4_cb, 0, sizeof(tHCI_H4_CB));
+ utils_queue_init(&(h4_cb.acl_rx_q));
+
+ /* Per HCI spec., always starts with 1 */
+ num_hci_cmd_pkts = 1;
+
+ /* Give an initial values of Host Controller's ACL data packet length
+ * Will update with an internal HCI(_LE)_Read_Buffer_Size request
+ */
+ h4_cb.hc_acl_data_size = 1021;
+ h4_cb.hc_ble_acl_data_size = 27;
+
+ btsnoop_init();
+}
+
+/*******************************************************************************
+**
+** Function hci_h4_cleanup
+**
+** Description Clean H4 module
+**
+** Returns None
+**
+*******************************************************************************/
+void hci_h4_cleanup(void)
+{
+ HCIDBG("hci_h4_cleanup");
+
+ btsnoop_close();
+ btsnoop_cleanup();
+}
+
+/*******************************************************************************
+**
+** Function hci_h4_send_msg
+**
+** Description Determine message type, set HCI H4 packet indicator, and
+** send message through USERIAL driver
+**
+** Returns None
+**
+*******************************************************************************/
+void hci_h4_send_msg(HC_BT_HDR *p_msg)
+{
+ uint8_t type = 0;
+ uint16_t handle;
+ uint16_t bytes_to_send, lay_spec;
+ uint8_t *p = ((uint8_t *)(p_msg + 1)) + p_msg->offset;
+ uint16_t event = p_msg->event & MSG_EVT_MASK;
+ uint16_t sub_event = p_msg->event & MSG_SUB_EVT_MASK;
+ uint16_t acl_pkt_size = 0, acl_data_size = 0;
+ uint16_t bytes_sent;
+
+ /* wake up BT device if its in sleep mode */
+ lpm_wake_assert();
+
+ if (event == MSG_STACK_TO_HC_HCI_ACL)
+ type = H4_TYPE_ACL_DATA;
+ else if (event == MSG_STACK_TO_HC_HCI_SCO)
+ type = H4_TYPE_SCO_DATA;
+ else if (event == MSG_STACK_TO_HC_HCI_CMD)
+ type = H4_TYPE_COMMAND;
+
+ if (sub_event == LOCAL_BR_EDR_CONTROLLER_ID)
+ {
+ acl_data_size = h4_cb.hc_acl_data_size;
+ acl_pkt_size = h4_cb.hc_acl_data_size + HCI_ACL_PREAMBLE_SIZE;
+ }
+ else
+ {
+ acl_data_size = h4_cb.hc_ble_acl_data_size;
+ acl_pkt_size = h4_cb.hc_ble_acl_data_size + HCI_ACL_PREAMBLE_SIZE;
+ }
+
+ /* Check if sending ACL data that needs fragmenting */
+ if ((event == MSG_STACK_TO_HC_HCI_ACL) && (p_msg->len > acl_pkt_size))
+ {
+ /* Get the handle from the packet */
+ STREAM_TO_UINT16 (handle, p);
+
+ /* Set packet boundary flags to "continuation packet" */
+ handle = (handle & 0xCFFF) | 0x1000;
+
+ /* Do all the first chunks */
+ while (p_msg->len > acl_pkt_size)
+ {
+ /* remember layer_specific because uart borrow
+ one byte from layer_specific for packet type */
+ lay_spec = p_msg->layer_specific;
+
+ p = ((uint8_t *)(p_msg + 1)) + p_msg->offset - 1;
+ *p = type;
+ bytes_to_send = acl_pkt_size + 1; /* packet_size + message type */
+
+ bytes_sent = userial_write(event,(uint8_t *) p,bytes_to_send);
+
+ /* generate snoop trace message */
+ btsnoop_capture(p_msg, FALSE);
+
+ p_msg->layer_specific = lay_spec;
+ /* Adjust offset and length for what we just sent */
+ p_msg->offset += acl_data_size;
+ p_msg->len -= acl_data_size;
+
+ p = ((uint8_t *)(p_msg + 1)) + p_msg->offset;
+
+ UINT16_TO_STREAM (p, handle);
+
+ if (p_msg->len > acl_pkt_size)
+ {
+ UINT16_TO_STREAM (p, acl_data_size);
+ }
+ else
+ {
+ UINT16_TO_STREAM (p, p_msg->len - HCI_ACL_PREAMBLE_SIZE);
+ }
+
+ /* If we were only to send partial buffer, stop when done. */
+ /* Send the buffer back to L2CAP to send the rest of it later */
+ if (p_msg->layer_specific)
+ {
+ if (--p_msg->layer_specific == 0)
+ {
+ p_msg->event = MSG_HC_TO_STACK_L2C_SEG_XMIT;
+
+ if (bt_hc_cbacks)
+ {
+ bt_hc_cbacks->tx_result((TRANSAC) p_msg, \
+ (char *) (p_msg + 1), \
+ BT_HC_TX_FRAGMENT);
+ }
+
+ return;
+ }
+ }
+ }
+ }
+
+
+ /* remember layer_specific because uart borrow
+ one byte from layer_specific for packet type */
+ lay_spec = p_msg->layer_specific;
+
+ /* Put the HCI Transport packet type 1 byte before the message */
+ p = ((uint8_t *)(p_msg + 1)) + p_msg->offset - 1;
+ *p = type;
+ bytes_to_send = p_msg->len + 1; /* message_size + message type */
+
+ bytes_sent = userial_write(event,(uint8_t *) p, bytes_to_send);
+
+ p_msg->layer_specific = lay_spec;
+
+ if (event == MSG_STACK_TO_HC_HCI_CMD)
+ {
+ num_hci_cmd_pkts--;
+
+ /* If this is an internal Cmd packet, the layer_specific field would
+ * have stored with the opcode of HCI command.
+ * Retrieve the opcode from the Cmd packet.
+ */
+ p++;
+ STREAM_TO_UINT16(lay_spec, p);
+ }
+
+ /* generate snoop trace message */
+ btsnoop_capture(p_msg, FALSE);
+
+ if (bt_hc_cbacks)
+ {
+ if ((event == MSG_STACK_TO_HC_HCI_CMD) && \
+ (h4_cb.int_cmd_rsp_pending > 0) && \
+ (p_msg->layer_specific == lay_spec))
+ {
+ /* dealloc buffer of internal command */
+ bt_hc_cbacks->dealloc((TRANSAC) p_msg, (char *) (p_msg + 1));
+ }
+ else
+ {
+ bt_hc_cbacks->tx_result((TRANSAC) p_msg, (char *) (p_msg + 1), \
+ BT_HC_TX_SUCCESS);
+ }
+ }
+
+ lpm_tx_done(TRUE);
+
+ return;
+}
+
+
+/*******************************************************************************
+**
+** Function hci_h4_receive_msg
+**
+** Description Construct HCI EVENT/ACL packets and send them to stack once
+** complete packet has been received.
+**
+** Returns Number of read bytes
+**
+*******************************************************************************/
+uint16_t hci_h4_receive_msg(void)
+{
+ uint16_t bytes_read = 0;
+ uint8_t byte;
+ uint16_t msg_len, len;
+ uint8_t msg_received;
+ tHCI_H4_CB *p_cb=&h4_cb;
+
+ while (TRUE)
+ {
+ /* Read one byte to see if there is anything waiting to be read */
+ if (userial_read(0 /*dummy*/, &byte, 1) == 0)
+ {
+ break;
+ }
+
+ bytes_read++;
+ msg_received = FALSE;
+
+ switch (p_cb->rcv_state)
+ {
+ case H4_RX_MSGTYPE_ST:
+ /* Start of new message */
+ if ((byte < H4_TYPE_ACL_DATA) || (byte > H4_TYPE_EVENT))
+ {
+ /* Unknown HCI message type */
+ /* Drop this byte */
+ ALOGE("[h4] Unknown HCI message type drop this byte 0x%x", byte);
+ break;
+ }
+
+ /* Initialize rx parameters */
+ p_cb->rcv_msg_type = byte;
+ p_cb->rcv_len = hci_preamble_table[byte-1];
+ memset(p_cb->preload_buffer, 0 , 6);
+ p_cb->preload_count = 0;
+ // p_cb->p_rcv_msg = NULL;
+ p_cb->rcv_state = H4_RX_LEN_ST; /* Next, wait for length to come */
+ break;
+
+ case H4_RX_LEN_ST:
+ /* Receiving preamble */
+ p_cb->preload_buffer[p_cb->preload_count++] = byte;
+ p_cb->rcv_len--;
+
+ /* Check if we received entire preamble yet */
+ if (p_cb->rcv_len == 0)
+ {
+ if (p_cb->rcv_msg_type == H4_TYPE_ACL_DATA)
+ {
+ /* ACL data lengths are 16-bits */
+ msg_len = p_cb->preload_buffer[3];
+ msg_len = (msg_len << 8) + p_cb->preload_buffer[2];
+
+ if (msg_len && (p_cb->preload_count == 4))
+ {
+ /* Check if this is a start packet */
+ byte = ((p_cb->preload_buffer[1] >> 4) & 0x03);
+
+ if (byte == ACL_RX_PKT_START)
+ {
+ /*
+ * A start packet & with non-zero data payload length.
+ * We want to read 2 more bytes to get L2CAP payload
+ * length.
+ */
+ p_cb->rcv_len = 2;
+
+ break;
+ }
+ }
+
+ /*
+ * Check for segmented packets. If this is a continuation
+ * packet, then we will continue appending data to the
+ * original rcv buffer.
+ */
+ p_cb->p_rcv_msg = acl_rx_frame_buffer_alloc();
+ }
+ else
+ {
+ /* Received entire preamble.
+ * Length is in the last received byte */
+ msg_len = byte;
+ p_cb->rcv_len = msg_len;
+
+ /* Allocate a buffer for message */
+ if (bt_hc_cbacks)
+ {
+ len = msg_len + p_cb->preload_count + BT_HC_HDR_SIZE;
+ p_cb->p_rcv_msg = \
+ (HC_BT_HDR *) bt_hc_cbacks->alloc(len);
+ }
+
+ if (p_cb->p_rcv_msg)
+ {
+ /* Initialize buffer with preloaded data */
+ p_cb->p_rcv_msg->offset = 0;
+ p_cb->p_rcv_msg->layer_specific = 0;
+ p_cb->p_rcv_msg->event = \
+ msg_evt_table[p_cb->rcv_msg_type-1];
+ p_cb->p_rcv_msg->len = p_cb->preload_count;
+ memcpy((uint8_t *)(p_cb->p_rcv_msg + 1), \
+ p_cb->preload_buffer, p_cb->preload_count);
+ }
+ }
+
+ if (p_cb->p_rcv_msg == NULL)
+ {
+ /* Unable to acquire message buffer. */
+ ALOGE( \
+ "H4: Unable to acquire buffer for incoming HCI message." \
+ );
+
+ if (msg_len == 0)
+ {
+ /* Wait for next message */
+ p_cb->rcv_state = H4_RX_MSGTYPE_ST;
+ }
+ else
+ {
+ /* Ignore rest of the packet */
+ p_cb->rcv_state = H4_RX_IGNORE_ST;
+ }
+
+ break;
+ }
+
+ /* Message length is valid */
+ if (msg_len)
+ {
+ /* Read rest of message */
+ p_cb->rcv_state = H4_RX_DATA_ST;
+ }
+ else
+ {
+ /* Message has no additional parameters.
+ * (Entire message has been received) */
+ if (p_cb->rcv_msg_type == H4_TYPE_ACL_DATA)
+ acl_rx_frame_end_chk(); /* to print snoop trace */
+
+ msg_received = TRUE;
+
+ /* Next, wait for next message */
+ p_cb->rcv_state = H4_RX_MSGTYPE_ST;
+ }
+ }
+ break;
+
+ case H4_RX_DATA_ST:
+ *((uint8_t *)(p_cb->p_rcv_msg + 1) + p_cb->p_rcv_msg->len++) = byte;
+ p_cb->rcv_len--;
+
+ if (p_cb->rcv_len > 0)
+ {
+ /* Read in the rest of the message */
+ len = userial_read(0 /*dummy*/, \
+ ((uint8_t *)(p_cb->p_rcv_msg+1) + p_cb->p_rcv_msg->len), \
+ p_cb->rcv_len);
+ p_cb->p_rcv_msg->len += len;
+ p_cb->rcv_len -= len;
+ bytes_read += len;
+ }
+
+ /* Check if we read in entire message yet */
+ if (p_cb->rcv_len == 0)
+ {
+ /* Received entire packet. */
+ /* Check for segmented l2cap packets */
+ if ((p_cb->rcv_msg_type == H4_TYPE_ACL_DATA) &&
+ !acl_rx_frame_end_chk())
+ {
+ /* Not the end of packet yet. */
+ /* Next, wait for next message */
+ p_cb->rcv_state = H4_RX_MSGTYPE_ST;
+ }
+ else
+ {
+ msg_received = TRUE;
+ /* Next, wait for next message */
+ p_cb->rcv_state = H4_RX_MSGTYPE_ST;
+ }
+ }
+ break;
+
+
+ case H4_RX_IGNORE_ST:
+ /* Ignore reset of packet */
+ p_cb->rcv_len--;
+
+ /* Check if we read in entire message yet */
+ if (p_cb->rcv_len == 0)
+ {
+ /* Next, wait for next message */
+ p_cb->rcv_state = H4_RX_MSGTYPE_ST;
+ }
+ break;
+ }
+
+
+ /* If we received entire message, then send it to the task */
+ if (msg_received)
+ {
+ uint8_t intercepted = FALSE;
+
+ /* generate snoop trace message */
+ /* ACL packet tracing had done in acl_rx_frame_end_chk() */
+ if (p_cb->p_rcv_msg->event != MSG_HC_TO_STACK_HCI_ACL)
+ btsnoop_capture(p_cb->p_rcv_msg, TRUE);
+
+ if (p_cb->p_rcv_msg->event == MSG_HC_TO_STACK_HCI_EVT)
+ intercepted = internal_event_intercept();
+
+ if ((bt_hc_cbacks) && (intercepted == FALSE))
+ {
+ bt_hc_cbacks->data_ind((TRANSAC) p_cb->p_rcv_msg, \
+ (char *) (p_cb->p_rcv_msg + 1), \
+ p_cb->p_rcv_msg->len + BT_HC_HDR_SIZE);
+ }
+ p_cb->p_rcv_msg = NULL;
+ }
+ }
+
+ return (bytes_read);
+}
+
+
+/*******************************************************************************
+**
+** Function hci_h4_send_int_cmd
+**
+** Description Place the internal commands (issued internally by vendor lib)
+** in the tx_q.
+**
+** Returns TRUE/FALSE
+**
+*******************************************************************************/
+uint8_t hci_h4_send_int_cmd(uint16_t opcode, HC_BT_HDR *p_buf, \
+ tINT_CMD_CBACK p_cback)
+{
+ if (h4_cb.int_cmd_rsp_pending > INT_CMD_PKT_MAX_COUNT)
+ {
+ ALOGE( \
+ "Allow only %d outstanding internal commands at a time [Reject 0x%04X]"\
+ , INT_CMD_PKT_MAX_COUNT, opcode);
+ return FALSE;
+ }
+
+ h4_cb.int_cmd_rsp_pending++;
+ h4_cb.int_cmd[h4_cb.int_cmd_wrt_idx].opcode = opcode;
+ h4_cb.int_cmd[h4_cb.int_cmd_wrt_idx].cback = p_cback;
+ h4_cb.int_cmd_wrt_idx = ((h4_cb.int_cmd_wrt_idx+1) & INT_CMD_PKT_IDX_MASK);
+
+ /* stamp signature to indicate an internal command */
+ p_buf->layer_specific = opcode;
+
+ utils_enqueue(&tx_q, (void *) p_buf);
+ bthc_signal_event(HC_EVENT_TX);
+
+ return TRUE;
+}
+
+
+/*******************************************************************************
+**
+** Function hci_h4_get_acl_data_length
+**
+** Description Issue HCI_READ_BUFFER_SIZE command to retrieve Controller's
+** ACL data length setting
+**
+** Returns None
+**
+*******************************************************************************/
+void hci_h4_get_acl_data_length(void)
+{
+ HC_BT_HDR *p_buf = NULL;
+ uint8_t *p, ret;
+
+ if (bt_hc_cbacks)
+ {
+ p_buf = (HC_BT_HDR *) bt_hc_cbacks->alloc(BT_HC_HDR_SIZE + \
+ HCI_CMD_PREAMBLE_SIZE);
+ }
+
+ if (p_buf)
+ {
+ p_buf->event = MSG_STACK_TO_HC_HCI_CMD;
+ p_buf->offset = 0;
+ p_buf->layer_specific = 0;
+ p_buf->len = HCI_CMD_PREAMBLE_SIZE;
+
+ p = (uint8_t *) (p_buf + 1);
+ UINT16_TO_STREAM(p, HCI_READ_BUFFER_SIZE);
+ *p = 0;
+
+ if ((ret = hci_h4_send_int_cmd(HCI_READ_BUFFER_SIZE, p_buf, \
+ get_acl_data_length_cback)) == FALSE)
+ {
+ bt_hc_cbacks->dealloc((TRANSAC) p_buf, (char *) (p_buf + 1));
+ }
+ else
+ return;
+ }
+
+ if (bt_hc_cbacks)
+ {
+ ALOGE("vendor lib postload aborted");
+ bt_hc_cbacks->postload_cb(NULL, BT_HC_POSTLOAD_FAIL);
+ }
+}
+
+
+/******************************************************************************
+** HCI H4 Services interface table
+******************************************************************************/
+
+const tHCI_IF hci_h4_func_table =
+{
+ hci_h4_init,
+ hci_h4_cleanup,
+ hci_h4_send_msg,
+ hci_h4_send_int_cmd,
+ hci_h4_get_acl_data_length,
+ hci_h4_receive_msg
+};
+
diff --git a/hci/src/hci_mct.c b/hci/src/hci_mct.c
new file mode 100755
index 0000000..4e70063
--- /dev/null
+++ b/hci/src/hci_mct.c
@@ -0,0 +1,1179 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2009-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * Filename: hci_mct.c
+ *
+ * Description: Contains HCI transport send/receive functions
+ * for Multi-Channels Transport
+ *
+ ******************************************************************************/
+
+#define LOG_TAG "bt_mct"
+
+#include <utils/Log.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include "bt_hci_bdroid.h"
+#include "hci.h"
+#include "userial.h"
+#include "utils.h"
+
+/******************************************************************************
+** Constants & Macros
+******************************************************************************/
+
+#ifndef HCI_DBG
+#define HCI_DBG FALSE
+#endif
+
+#if (HCI_DBG == TRUE)
+#define HCIDBG(param, ...) {LOGD(param, ## __VA_ARGS__);}
+#else
+#define HCIDBG(param, ...) {}
+#endif
+
+/* Preamble length for HCI Commands:
+** 2-bytes for opcode and 1 byte for length
+*/
+#define HCI_CMD_PREAMBLE_SIZE 3
+
+/* Preamble length for HCI Events:
+** 1-byte for opcode and 1 byte for length
+*/
+#define HCI_EVT_PREAMBLE_SIZE 2
+
+/* Preamble length for SCO Data:
+** 2-byte for Handle and 1 byte for length
+*/
+#define HCI_SCO_PREAMBLE_SIZE 3
+
+/* Preamble length for ACL Data:
+** 2-byte for Handle and 2 byte for length
+*/
+#define HCI_ACL_PREAMBLE_SIZE 4
+
+#define ACL_RX_PKT_START 2
+#define ACL_RX_PKT_CONTINUE 1
+#define L2CAP_HEADER_SIZE 4
+
+/* Maximum numbers of allowed internal
+** outstanding command packets at any time
+*/
+#define INT_CMD_PKT_MAX_COUNT 8
+#define INT_CMD_PKT_IDX_MASK 0x07
+
+#define HCI_COMMAND_COMPLETE_EVT 0x0E
+#define HCI_COMMAND_STATUS_EVT 0x0F
+#define HCI_READ_BUFFER_SIZE 0x1005
+#define HCI_LE_READ_BUFFER_SIZE 0x2002
+
+/******************************************************************************
+** Local type definitions
+******************************************************************************/
+
+/* MCT Rx States */
+typedef enum {
+ MCT_RX_NEWMSG_ST,
+ MCT_RX_LEN_ST,
+ MCT_RX_DATA_ST,
+ MCT_RX_IGNORE_ST
+} tHCI_MCT_RCV_STATE;
+
+/* Callback function for the returned event of internal issued command */
+typedef void (*tINT_CMD_CBACK)(void *p_mem);
+
+typedef struct
+{
+ uint16_t opcode; /* OPCODE of outstanding internal commands */
+ tINT_CMD_CBACK cback; /* Callback function when return of internal
+ * command is received */
+} tINT_CMD_Q;
+
+typedef struct
+{
+ HC_BT_HDR *p_rcv_msg; /* Buffer to hold current rx HCI message */
+ uint16_t rcv_len; /* Size of current incoming message */
+ tHCI_MCT_RCV_STATE rcv_state; /* Receive state of current rx message */
+ uint8_t preload_count; /* Count numbers of preload bytes */
+ uint8_t preload_buffer[6]; /* HCI_ACL_PREAMBLE_SIZE + 2 */
+} tHCI_RCV_CB;
+
+/* Control block for HCISU_MCT */
+typedef struct
+{
+ tHCI_RCV_CB rcv_evt;
+ tHCI_RCV_CB rcv_acl;
+ uint16_t hc_acl_data_size; /* Controller's max ACL data length */
+ uint16_t hc_ble_acl_data_size; /* Controller's max BLE ACL data length */
+ BUFFER_Q acl_rx_q; /* Queue of base buffers for fragmented ACL pkts */
+ int int_cmd_rsp_pending; /* Num of internal cmds pending for ack */
+ uint8_t int_cmd_rd_idx; /* Read index of int_cmd_opcode queue */
+ uint8_t int_cmd_wrt_idx; /* Write index of int_cmd_opcode queue */
+ tINT_CMD_Q int_cmd[INT_CMD_PKT_MAX_COUNT]; /* FIFO queue */
+} tHCI_MCT_CB;
+
+/******************************************************************************
+** Externs
+******************************************************************************/
+
+extern BUFFER_Q tx_q;
+
+void btsnoop_init(void);
+void btsnoop_close(void);
+void btsnoop_cleanup (void);
+void btsnoop_capture(HC_BT_HDR *p_buf, uint8_t is_rcvd);
+uint8_t hci_mct_send_int_cmd(uint16_t opcode, HC_BT_HDR *p_buf, \
+ tINT_CMD_CBACK p_cback);
+void lpm_wake_assert(void);
+void lpm_tx_done(uint8_t is_tx_done);
+
+/******************************************************************************
+** Variables
+******************************************************************************/
+
+/* Num of allowed outstanding HCI CMD packets */
+volatile int num_hci_cmd_pkts = 1;
+
+/******************************************************************************
+** Static variables
+******************************************************************************/
+
+static tHCI_MCT_CB mct_cb;
+
+/******************************************************************************
+** Static functions
+******************************************************************************/
+
+/*******************************************************************************
+**
+** Function get_acl_data_length_cback
+**
+** Description Callback function for HCI_READ_BUFFER_SIZE and
+** HCI_LE_READ_BUFFER_SIZE commands if they were sent because
+** of internal request.
+**
+** Returns None
+**
+*******************************************************************************/
+void get_acl_data_length_cback(void *p_mem)
+{
+ uint8_t *p, status;
+ uint16_t opcode, len=0;
+ HC_BT_HDR *p_buf = (HC_BT_HDR *) p_mem;
+
+ p = (uint8_t *)(p_buf + 1) + 3;
+ STREAM_TO_UINT16(opcode, p)
+ status = *p++;
+ if (status == 0) /* Success */
+ STREAM_TO_UINT16(len, p)
+
+ if (opcode == HCI_READ_BUFFER_SIZE)
+ {
+ if (status == 0)
+ mct_cb.hc_acl_data_size = len;
+
+ /* reuse the rx buffer for sending HCI_LE_READ_BUFFER_SIZE command */
+ p_buf->event = MSG_STACK_TO_HC_HCI_CMD;
+ p_buf->offset = 0;
+ p_buf->layer_specific = 0;
+ p_buf->len = 3;
+
+ p = (uint8_t *) (p_buf + 1);
+ UINT16_TO_STREAM(p, HCI_LE_READ_BUFFER_SIZE);
+ *p = 0;
+
+ if ((status = hci_mct_send_int_cmd(HCI_LE_READ_BUFFER_SIZE, p_buf, \
+ get_acl_data_length_cback)) == FALSE)
+ {
+ bt_hc_cbacks->dealloc((TRANSAC) p_buf, (char *) (p_buf + 1));
+ bt_hc_cbacks->postload_cb(NULL, BT_HC_POSTLOAD_SUCCESS);
+ }
+ }
+ else if (opcode == HCI_LE_READ_BUFFER_SIZE)
+ {
+ if (status == 0)
+ mct_cb.hc_ble_acl_data_size = (len) ? len : mct_cb.hc_acl_data_size;
+
+ if (bt_hc_cbacks)
+ {
+ bt_hc_cbacks->dealloc((TRANSAC) p_buf, (char *) (p_buf + 1));
+ ALOGE("hci lib postload completed");
+ bt_hc_cbacks->postload_cb(NULL, BT_HC_POSTLOAD_SUCCESS);
+ }
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function internal_event_intercept
+**
+** Description This function is called to parse received HCI event and
+** - update the Num_HCI_Command_Packets
+** - intercept the event if it is the result of an early
+** issued internal command.
+**
+** Returns TRUE : if the event had been intercepted for internal process
+** FALSE : send this event to core stack
+**
+*******************************************************************************/
+uint8_t internal_event_intercept(void)
+{
+ uint8_t *p;
+ uint8_t event_code;
+ uint16_t opcode, len;
+ tHCI_MCT_CB *p_cb = &mct_cb;
+
+ p = (uint8_t *)(p_cb->rcv_evt.p_rcv_msg + 1);
+
+ event_code = *p++;
+ len = *p++;
+
+ if (event_code == HCI_COMMAND_COMPLETE_EVT)
+ {
+ utils_lock();
+ num_hci_cmd_pkts = *p++;
+ utils_unlock();
+
+ // Signal TX event so the worker thread can check if it has anything
+ // to send
+ bthc_signal_event(HC_EVENT_TX);
+
+ if (p_cb->int_cmd_rsp_pending > 0)
+ {
+ STREAM_TO_UINT16(opcode, p)
+
+ if (opcode == p_cb->int_cmd[p_cb->int_cmd_rd_idx].opcode)
+ {
+ HCIDBG( \
+ "Intercept CommandCompleteEvent for internal command (0x%04X)",\
+ opcode);
+ if (p_cb->int_cmd[p_cb->int_cmd_rd_idx].cback != NULL)
+ {
+ p_cb->int_cmd[p_cb->int_cmd_rd_idx].cback(p_cb->rcv_evt.p_rcv_msg);
+ }
+ else
+ {
+ // Missing cback function!
+ // Release the p_rcv_msg buffer.
+ if (bt_hc_cbacks)
+ {
+ bt_hc_cbacks->dealloc((TRANSAC) p_cb->rcv_evt.p_rcv_msg, \
+ (char *) (p_cb->rcv_evt.p_rcv_msg + 1));
+ }
+ }
+ p_cb->int_cmd_rd_idx = ((p_cb->int_cmd_rd_idx+1) & \
+ INT_CMD_PKT_IDX_MASK);
+ p_cb->int_cmd_rsp_pending--;
+ return TRUE;
+ }
+ }
+ }
+ else if (event_code == HCI_COMMAND_STATUS_EVT)
+ {
+ utils_lock();
+ num_hci_cmd_pkts = *(++p);
+ utils_unlock();
+
+ // Signal TX event so the worker thread can check if it has anything
+ // to send
+ bthc_signal_event(HC_EVENT_TX);
+ }
+
+ return FALSE;
+}
+
+/*******************************************************************************
+**
+** Function acl_rx_frame_buffer_alloc
+**
+** Description This function is called from the HCI transport when the
+** first 4 or 6 bytes of an HCI ACL packet have been received:
+** - Allocate a new buffer if it is a start pakcet of L2CAP
+** message.
+** - Return the buffer address of the starting L2CAP message
+** frame if the packet is the next segment of a fragmented
+** L2CAP message.
+**
+** Returns the address of the receive buffer caller should use
+** (CR419: Modified to return NULL in case of error.)
+**
+** NOTE This assumes that the L2CAP MTU size is less than the size
+** of an HCI ACL buffer, so the maximum L2CAP message will fit
+** into one buffer.
+**
+*******************************************************************************/
+static HC_BT_HDR *acl_rx_frame_buffer_alloc (void)
+{
+ uint8_t *p;
+ uint16_t handle;
+ uint16_t hci_len;
+ uint16_t total_len;
+ uint8_t pkt_type;
+ HC_BT_HDR *p_return_buf = NULL;
+ tHCI_MCT_CB *p_cb = &mct_cb;
+
+
+ p = p_cb->rcv_acl.preload_buffer;
+
+ STREAM_TO_UINT16 (handle, p);
+ STREAM_TO_UINT16 (hci_len, p);
+ STREAM_TO_UINT16 (total_len, p);
+
+ pkt_type = (uint8_t)(((handle) >> 12) & 0x0003);
+ handle = (uint16_t)((handle) & 0x0FFF);
+
+ if (p_cb->acl_rx_q.count)
+ {
+ uint16_t save_handle;
+ HC_BT_HDR *p_hdr = p_cb->acl_rx_q.p_first;
+
+ while (p_hdr != NULL)
+ {
+ p = (uint8_t *)(p_hdr + 1);
+ STREAM_TO_UINT16 (save_handle, p);
+ save_handle = (uint16_t)((save_handle) & 0x0FFF);
+ if (save_handle == handle)
+ {
+ p_return_buf = p_hdr;
+ break;
+ }
+ p_hdr = utils_getnext(p_hdr);
+ }
+ }
+
+ if (pkt_type == ACL_RX_PKT_START) /*** START PACKET ***/
+ {
+ /* Might have read 2 bytes for the L2CAP payload length */
+ p_cb->rcv_acl.rcv_len = (hci_len) ? (hci_len - 2) : 0;
+
+ /* Start of packet. If we were in the middle of receiving */
+ /* a packet on the same ACL handle, the original packet is incomplete.
+ * Drop it. */
+ if (p_return_buf)
+ {
+ ALOGW("dropping incomplete ACL frame");
+
+ utils_remove_from_queue(&(p_cb->acl_rx_q), p_return_buf);
+
+ if (bt_hc_cbacks)
+ {
+ bt_hc_cbacks->dealloc((TRANSAC) p_return_buf, \
+ (char *) (p_return_buf + 1));
+ }
+ p_return_buf = NULL;
+ }
+
+ /* Allocate a buffer for message */
+ if (bt_hc_cbacks)
+ {
+ int len = total_len + HCI_ACL_PREAMBLE_SIZE + L2CAP_HEADER_SIZE + \
+ BT_HC_HDR_SIZE;
+ p_return_buf = (HC_BT_HDR *) bt_hc_cbacks->alloc(len);
+ }
+
+ if (p_return_buf)
+ {
+ /* Initialize buffer with preloaded data */
+ p_return_buf->offset = 0;
+ p_return_buf->layer_specific = 0;
+ p_return_buf->event = MSG_HC_TO_STACK_HCI_ACL;
+ p_return_buf->len = p_cb->rcv_acl.preload_count;
+ memcpy((uint8_t *)(p_return_buf + 1), p_cb->rcv_acl.preload_buffer, \
+ p_cb->rcv_acl.preload_count);
+
+ if (hci_len && ((total_len + L2CAP_HEADER_SIZE) > hci_len))
+ {
+ /* Will expect to see fragmented ACL packets */
+ /* Keep the base buffer address in the watching queue */
+ utils_enqueue(&(p_cb->acl_rx_q), p_return_buf);
+ }
+ }
+ }
+ else /*** CONTINUATION PACKET ***/
+ {
+ p_cb->rcv_acl.rcv_len = hci_len;
+
+ if (p_return_buf)
+ {
+ /* Packet continuation and found the original rx buffer */
+ uint8_t *p_f = p = (uint8_t *)(p_return_buf + 1) + 2;
+
+ STREAM_TO_UINT16 (total_len, p);
+
+ /* Update HCI header of first segment (base buffer) with new len */
+ total_len += hci_len;
+ UINT16_TO_STREAM (p_f, total_len);
+ }
+ }
+
+ return (p_return_buf);
+}
+
+/*******************************************************************************
+**
+** Function acl_rx_frame_end_chk
+**
+** Description This function is called from the HCI transport when the last
+** byte of an HCI ACL packet has been received. It checks if
+** the L2CAP message is complete, i.e. no more continuation
+** packets are expected.
+**
+** Returns TRUE if message complete, FALSE if continuation expected
+**
+*******************************************************************************/
+static uint8_t acl_rx_frame_end_chk (void)
+{
+ uint8_t *p;
+ uint16_t handle, hci_len, l2cap_len;
+ HC_BT_HDR *p_buf;
+ tHCI_MCT_CB *p_cb = &mct_cb;
+ uint8_t frame_end=TRUE;
+
+ p_buf = p_cb->rcv_acl.p_rcv_msg;
+ p = (uint8_t *)(p_buf + 1);
+
+ STREAM_TO_UINT16 (handle, p);
+ STREAM_TO_UINT16 (hci_len, p);
+ STREAM_TO_UINT16 (l2cap_len, p);
+
+ if (hci_len > 0)
+ {
+ if (l2cap_len > (p_buf->len-(HCI_ACL_PREAMBLE_SIZE+L2CAP_HEADER_SIZE)) )
+ {
+ /* If the L2CAP length has not been reached, tell caller not to send
+ * this buffer to stack */
+ frame_end = FALSE;
+ }
+ else
+ {
+ /*
+ * The current buffer coulb be in the watching list.
+ * Remove it from the list if it is in.
+ */
+ if (p_cb->acl_rx_q.count)
+ utils_remove_from_queue(&(p_cb->acl_rx_q), p_buf);
+ }
+ }
+
+ /****
+ ** Print snoop trace
+ ****/
+ if (p_buf->offset)
+ {
+ /* CONTINUATION PACKET */
+
+ /* save original p_buf->len content */
+ uint16_t tmp_u16 = p_buf->len;
+
+ /* borrow HCI_ACL_PREAMBLE_SIZE bytes from the payload section */
+ p = (uint8_t *)(p_buf + 1) + p_buf->offset - HCI_ACL_PREAMBLE_SIZE;
+
+ /* save contents */
+ memcpy(p_cb->rcv_acl.preload_buffer, p, HCI_ACL_PREAMBLE_SIZE);
+
+ /* Set packet boundary flags to "continuation packet" */
+ handle = (handle & 0xCFFF) | 0x1000;
+
+ /* write handl & length info */
+ UINT16_TO_STREAM (p, handle);
+ UINT16_TO_STREAM (p, (p_buf->len - p_buf->offset));
+
+ /* roll pointer back */
+ p = p - HCI_ACL_PREAMBLE_SIZE;
+
+ /* adjust `p_buf->offset` & `p_buf->len`
+ * before calling btsnoop_capture() */
+ p_buf->offset = p_buf->offset - HCI_ACL_PREAMBLE_SIZE;
+ p_buf->len = p_buf->len - p_buf->offset;
+
+ btsnoop_capture(p_buf, TRUE);
+
+ /* restore contents */
+ memcpy(p, p_cb->rcv_acl.preload_buffer, HCI_ACL_PREAMBLE_SIZE);
+
+ /* restore p_buf->len */
+ p_buf->len = tmp_u16;
+ }
+ else
+ {
+ /* START PACKET */
+ btsnoop_capture(p_buf, TRUE);
+ }
+
+ if (frame_end == TRUE)
+ p_buf->offset = 0;
+ else
+ p_buf->offset = p_buf->len; /* save current buffer-end position */
+
+ return frame_end;
+}
+
+/*****************************************************************************
+** HCI MCT INTERFACE FUNCTIONS
+*****************************************************************************/
+
+/*******************************************************************************
+**
+** Function hci_mct_init
+**
+** Description Initialize MCT module
+**
+** Returns None
+**
+*******************************************************************************/
+void hci_mct_init(void)
+{
+ HCIDBG("hci_mct_init");
+
+ memset(&mct_cb, 0, sizeof(tHCI_MCT_CB));
+ utils_queue_init(&(mct_cb.acl_rx_q));
+
+ /* Per HCI spec., always starts with 1 */
+ num_hci_cmd_pkts = 1;
+
+ /* Give an initial values of Host Controller's ACL data packet length
+ * Will update with an internal HCI(_LE)_Read_Buffer_Size request
+ */
+ mct_cb.hc_acl_data_size = 1021;
+ mct_cb.hc_ble_acl_data_size = 27;
+
+ btsnoop_init();
+}
+
+/*******************************************************************************
+**
+** Function hci_mct_cleanup
+**
+** Description Clean MCT module
+**
+** Returns None
+**
+*******************************************************************************/
+void hci_mct_cleanup(void)
+{
+ HCIDBG("hci_mct_cleanup");
+
+ btsnoop_close();
+ btsnoop_cleanup();
+}
+
+/*******************************************************************************
+**
+** Function hci_mct_send_msg
+**
+** Description Determine message type, then send message through according
+** channel
+**
+** Returns None
+**
+*******************************************************************************/
+void hci_mct_send_msg(HC_BT_HDR *p_msg)
+{
+ uint16_t handle;
+ uint16_t lay_spec;
+ uint8_t *p = ((uint8_t *)(p_msg + 1)) + p_msg->offset;
+ uint16_t event = p_msg->event & MSG_EVT_MASK;
+ uint16_t sub_event = p_msg->event & MSG_SUB_EVT_MASK;
+ uint16_t acl_pkt_size = 0, acl_data_size = 0;
+
+ /* wake up BT device if its in sleep mode */
+ lpm_wake_assert();
+
+ if (sub_event == LOCAL_BR_EDR_CONTROLLER_ID)
+ {
+ acl_data_size = mct_cb.hc_acl_data_size;
+ acl_pkt_size = mct_cb.hc_acl_data_size + HCI_ACL_PREAMBLE_SIZE;
+ }
+ else
+ {
+ acl_data_size = mct_cb.hc_ble_acl_data_size;
+ acl_pkt_size = mct_cb.hc_ble_acl_data_size + HCI_ACL_PREAMBLE_SIZE;
+ }
+
+ /* Check if sending ACL data that needs fragmenting */
+ if ((event == MSG_STACK_TO_HC_HCI_ACL) && (p_msg->len > acl_pkt_size))
+ {
+ /* Get the handle from the packet */
+ STREAM_TO_UINT16 (handle, p);
+
+ /* Set packet boundary flags to "continuation packet" */
+ handle = (handle & 0xCFFF) | 0x1000;
+
+ /* Do all the first chunks */
+ while (p_msg->len > acl_pkt_size)
+ {
+ p = ((uint8_t *)(p_msg + 1)) + p_msg->offset;
+
+ userial_write(event, (uint8_t *) p, acl_pkt_size);
+
+ /* generate snoop trace message */
+ btsnoop_capture(p_msg, FALSE);
+
+ /* Adjust offset and length for what we just sent */
+ p_msg->offset += acl_data_size;
+ p_msg->len -= acl_data_size;
+
+ p = ((uint8_t *)(p_msg + 1)) + p_msg->offset;
+
+ UINT16_TO_STREAM (p, handle);
+
+ if (p_msg->len > acl_pkt_size)
+ {
+ UINT16_TO_STREAM (p, acl_data_size);
+ }
+ else
+ {
+ UINT16_TO_STREAM (p, p_msg->len - HCI_ACL_PREAMBLE_SIZE);
+ }
+
+ /* If we were only to send partial buffer, stop when done. */
+ /* Send the buffer back to L2CAP to send the rest of it later */
+ if (p_msg->layer_specific)
+ {
+ if (--p_msg->layer_specific == 0)
+ {
+ p_msg->event = MSG_HC_TO_STACK_L2C_SEG_XMIT;
+
+ if (bt_hc_cbacks)
+ {
+ bt_hc_cbacks->tx_result((TRANSAC) p_msg, \
+ (char *) (p_msg + 1), \
+ BT_HC_TX_FRAGMENT);
+ }
+
+ return;
+ }
+ }
+ }
+ }
+
+ p = ((uint8_t *)(p_msg + 1)) + p_msg->offset;
+
+ if (event == MSG_STACK_TO_HC_HCI_CMD)
+ {
+ uint8_t *p_tmp = p;
+
+ utils_lock();
+ num_hci_cmd_pkts--;
+ utils_unlock();
+
+ /* If this is an internal Cmd packet, the layer_specific field would
+ * have stored with the opcode of HCI command.
+ * Retrieve the opcode from the Cmd packet.
+ */
+ p_tmp++;
+ STREAM_TO_UINT16(lay_spec, p_tmp);
+ }
+
+ userial_write(event, (uint8_t *) p, p_msg->len);
+
+
+ /* generate snoop trace message */
+ btsnoop_capture(p_msg, FALSE);
+
+ if (bt_hc_cbacks)
+ {
+ if ((event == MSG_STACK_TO_HC_HCI_CMD) && \
+ (mct_cb.int_cmd_rsp_pending > 0) && \
+ (p_msg->layer_specific == lay_spec))
+ {
+ /* dealloc buffer of internal command */
+ bt_hc_cbacks->dealloc((TRANSAC) p_msg, (char *) (p_msg + 1));
+ }
+ else
+ {
+ bt_hc_cbacks->tx_result((TRANSAC) p_msg, (char *) (p_msg + 1), \
+ BT_HC_TX_SUCCESS);
+ }
+ }
+
+ lpm_tx_done(TRUE);
+
+ return;
+}
+
+
+/*******************************************************************************
+**
+** Function hci_mct_receive_evt_msg
+**
+** Description Construct HCI EVENT packet
+**
+** Returns Number of read bytes
+**
+*******************************************************************************/
+uint16_t hci_mct_receive_evt_msg(void)
+{
+ uint16_t bytes_read = 0;
+ uint8_t byte;
+ uint16_t msg_len, len;
+ uint8_t msg_received;
+ tHCI_RCV_CB *p_cb=&mct_cb.rcv_evt;
+ uint8_t continue_fetch_looping = TRUE;
+
+ while (continue_fetch_looping)
+ {
+ /* Read one byte to see if there is anything waiting to be read */
+ if (userial_read(MSG_HC_TO_STACK_HCI_EVT, &byte, 1) == 0)
+ {
+ break;
+ }
+
+ bytes_read++;
+ msg_received = FALSE;
+
+ switch (p_cb->rcv_state)
+ {
+ case MCT_RX_NEWMSG_ST:
+ /* Start of new message */
+ /* Initialize rx parameters */
+ memset(p_cb->preload_buffer, 0 , 6);
+ p_cb->preload_buffer[0] = byte;
+ p_cb->preload_count = 1;
+ p_cb->rcv_len = HCI_EVT_PREAMBLE_SIZE - 1;
+ // p_cb->p_rcv_msg = NULL;
+ p_cb->rcv_state = MCT_RX_LEN_ST; /* Next, wait for length to come */
+ break;
+
+ case MCT_RX_LEN_ST:
+ /* Receiving preamble */
+ p_cb->preload_buffer[p_cb->preload_count++] = byte;
+ p_cb->rcv_len--;
+
+ /* Check if we received entire preamble yet */
+ if (p_cb->rcv_len == 0)
+ {
+ /* Received entire preamble.
+ * Length is in the last received byte */
+ msg_len = byte;
+ p_cb->rcv_len = msg_len;
+
+ /* Allocate a buffer for message */
+ if (bt_hc_cbacks)
+ {
+ len = msg_len + p_cb->preload_count + BT_HC_HDR_SIZE;
+ p_cb->p_rcv_msg = \
+ (HC_BT_HDR *) bt_hc_cbacks->alloc(len);
+ }
+
+ if (p_cb->p_rcv_msg)
+ {
+ /* Initialize buffer with preloaded data */
+ p_cb->p_rcv_msg->offset = 0;
+ p_cb->p_rcv_msg->layer_specific = 0;
+ p_cb->p_rcv_msg->event = MSG_HC_TO_STACK_HCI_EVT;
+ p_cb->p_rcv_msg->len = p_cb->preload_count;
+ memcpy((uint8_t *)(p_cb->p_rcv_msg + 1), \
+ p_cb->preload_buffer, p_cb->preload_count);
+ }
+ else
+ {
+ /* Unable to acquire message buffer. */
+ ALOGE( \
+ "Unable to acquire buffer for incoming HCI message." \
+ );
+
+ if (msg_len == 0)
+ {
+ /* Wait for next message */
+ p_cb->rcv_state = MCT_RX_NEWMSG_ST;
+ continue_fetch_looping = FALSE;
+ }
+ else
+ {
+ /* Ignore rest of the packet */
+ p_cb->rcv_state = MCT_RX_IGNORE_ST;
+ }
+
+ break;
+ }
+
+ /* Message length is valid */
+ if (msg_len)
+ {
+ /* Read rest of message */
+ p_cb->rcv_state = MCT_RX_DATA_ST;
+ }
+ else
+ {
+ /* Message has no additional parameters.
+ * (Entire message has been received) */
+ msg_received = TRUE;
+
+ /* Next, wait for next message */
+ p_cb->rcv_state = MCT_RX_NEWMSG_ST;
+ continue_fetch_looping = FALSE;
+ }
+ }
+ break;
+
+ case MCT_RX_DATA_ST:
+ *((uint8_t *)(p_cb->p_rcv_msg + 1) + p_cb->p_rcv_msg->len++) = byte;
+ p_cb->rcv_len--;
+
+ if (p_cb->rcv_len > 0)
+ {
+ /* Read in the rest of the message */
+ len = userial_read(MSG_HC_TO_STACK_HCI_EVT, \
+ ((uint8_t *)(p_cb->p_rcv_msg+1) + p_cb->p_rcv_msg->len), \
+ p_cb->rcv_len);
+ p_cb->p_rcv_msg->len += len;
+ p_cb->rcv_len -= len;
+ bytes_read += len;
+ }
+
+ /* Check if we read in entire message yet */
+ if (p_cb->rcv_len == 0)
+ {
+ /* Received entire packet. */
+ msg_received = TRUE;
+ /* Next, wait for next message */
+ p_cb->rcv_state = MCT_RX_NEWMSG_ST;
+ continue_fetch_looping = FALSE;
+ }
+ break;
+
+
+ case MCT_RX_IGNORE_ST:
+ /* Ignore reset of packet */
+ p_cb->rcv_len--;
+
+ /* Check if we read in entire message yet */
+ if (p_cb->rcv_len == 0)
+ {
+ /* Next, wait for next message */
+ p_cb->rcv_state = MCT_RX_NEWMSG_ST;
+ continue_fetch_looping = FALSE;
+ }
+ break;
+ }
+
+
+ /* If we received entire message, then send it to the task */
+ if (msg_received)
+ {
+ uint8_t intercepted = FALSE;
+
+ /* generate snoop trace message */
+ btsnoop_capture(p_cb->p_rcv_msg, TRUE);
+
+ intercepted = internal_event_intercept();
+
+ if ((bt_hc_cbacks) && (intercepted == FALSE))
+ {
+ bt_hc_cbacks->data_ind((TRANSAC) p_cb->p_rcv_msg, \
+ (char *) (p_cb->p_rcv_msg + 1), \
+ p_cb->p_rcv_msg->len + BT_HC_HDR_SIZE);
+ }
+ p_cb->p_rcv_msg = NULL;
+ }
+ }
+
+ return (bytes_read);
+}
+
+
+/*******************************************************************************
+**
+** Function hci_mct_receive_acl_msg
+**
+** Description Construct HCI ACL packet
+**
+** Returns Number of read bytes
+**
+*******************************************************************************/
+uint16_t hci_mct_receive_acl_msg(void)
+{
+ uint16_t bytes_read = 0;
+ uint8_t byte;
+ uint16_t msg_len, len;
+ uint8_t msg_received;
+ tHCI_RCV_CB *p_cb=&mct_cb.rcv_acl;
+ uint8_t continue_fetch_looping = TRUE;
+
+ while (continue_fetch_looping)
+ {
+ /* Read one byte to see if there is anything waiting to be read */
+ if (userial_read(MSG_HC_TO_STACK_HCI_ACL, &byte, 1) == 0)
+ {
+ break;
+ }
+
+ bytes_read++;
+ msg_received = FALSE;
+
+ switch (p_cb->rcv_state)
+ {
+ case MCT_RX_NEWMSG_ST:
+ /* Start of new message */
+ /* Initialize rx parameters */
+ memset(p_cb->preload_buffer, 0 , 6);
+ p_cb->preload_buffer[0] = byte;
+ p_cb->preload_count = 1;
+ p_cb->rcv_len = HCI_ACL_PREAMBLE_SIZE - 1;
+ // p_cb->p_rcv_msg = NULL;
+ p_cb->rcv_state = MCT_RX_LEN_ST; /* Next, wait for length to come */
+ break;
+
+ case MCT_RX_LEN_ST:
+ /* Receiving preamble */
+ p_cb->preload_buffer[p_cb->preload_count++] = byte;
+ p_cb->rcv_len--;
+
+ /* Check if we received entire preamble yet */
+ if (p_cb->rcv_len == 0)
+ {
+ /* ACL data lengths are 16-bits */
+ msg_len = p_cb->preload_buffer[3];
+ msg_len = (msg_len << 8) + p_cb->preload_buffer[2];
+
+ if (msg_len && (p_cb->preload_count == 4))
+ {
+ /* Check if this is a start packet */
+ byte = ((p_cb->preload_buffer[1] >> 4) & 0x03);
+
+ if (byte == ACL_RX_PKT_START)
+ {
+ /*
+ * A start packet & with non-zero data payload length.
+ * We want to read 2 more bytes to get L2CAP payload
+ * length.
+ */
+ p_cb->rcv_len = 2;
+
+ break;
+ }
+ }
+
+ /*
+ * Check for segmented packets. If this is a continuation
+ * packet, then we will continue appending data to the
+ * original rcv buffer.
+ */
+ p_cb->p_rcv_msg = acl_rx_frame_buffer_alloc();
+
+ if (p_cb->p_rcv_msg == NULL)
+ {
+ /* Unable to acquire message buffer. */
+ ALOGE( \
+ "Unable to acquire buffer for incoming HCI message." \
+ );
+
+ if (msg_len == 0)
+ {
+ /* Wait for next message */
+ p_cb->rcv_state = MCT_RX_NEWMSG_ST;
+ continue_fetch_looping = FALSE;
+ }
+ else
+ {
+ /* Ignore rest of the packet */
+ p_cb->rcv_state = MCT_RX_IGNORE_ST;
+ }
+
+ break;
+ }
+
+ /* Message length is valid */
+ if (msg_len)
+ {
+ /* Read rest of message */
+ p_cb->rcv_state = MCT_RX_DATA_ST;
+ }
+ else
+ {
+ /* Message has no additional parameters.
+ * (Entire message has been received) */
+ acl_rx_frame_end_chk(); /* to print snoop trace */
+
+ msg_received = TRUE;
+
+ /* Next, wait for next message */
+ p_cb->rcv_state = MCT_RX_NEWMSG_ST;
+ continue_fetch_looping = FALSE;
+ }
+ }
+ break;
+
+ case MCT_RX_DATA_ST:
+ *((uint8_t *)(p_cb->p_rcv_msg + 1) + p_cb->p_rcv_msg->len++) = byte;
+ p_cb->rcv_len--;
+
+ if (p_cb->rcv_len > 0)
+ {
+ /* Read in the rest of the message */
+ len = userial_read(MSG_HC_TO_STACK_HCI_ACL, \
+ ((uint8_t *)(p_cb->p_rcv_msg+1) + p_cb->p_rcv_msg->len), \
+ p_cb->rcv_len);
+ p_cb->p_rcv_msg->len += len;
+ p_cb->rcv_len -= len;
+ bytes_read += len;
+ }
+
+ /* Check if we read in entire message yet */
+ if (p_cb->rcv_len == 0)
+ {
+ /* Received entire packet. */
+ /* Check for segmented l2cap packets */
+ if (acl_rx_frame_end_chk())
+ {
+ msg_received = TRUE;
+ }
+
+ /* Next, wait for next message */
+ p_cb->rcv_state = MCT_RX_NEWMSG_ST;
+ continue_fetch_looping = FALSE;
+ }
+ break;
+
+
+ case MCT_RX_IGNORE_ST:
+ /* Ignore reset of packet */
+ p_cb->rcv_len--;
+
+ /* Check if we read in entire message yet */
+ if (p_cb->rcv_len == 0)
+ {
+ /* Next, wait for next message */
+ p_cb->rcv_state = MCT_RX_NEWMSG_ST;
+ continue_fetch_looping = FALSE;
+ }
+ break;
+ }
+
+
+ /* If we received entire message, then send it to the task */
+ if (msg_received)
+ {
+ if (bt_hc_cbacks)
+ {
+ bt_hc_cbacks->data_ind((TRANSAC) p_cb->p_rcv_msg, \
+ (char *) (p_cb->p_rcv_msg + 1), \
+ p_cb->p_rcv_msg->len + BT_HC_HDR_SIZE);
+ }
+ p_cb->p_rcv_msg = NULL;
+ }
+ }
+
+ return (bytes_read);
+}
+
+/*******************************************************************************
+**
+** Function hci_mct_send_int_cmd
+**
+** Description Place the internal commands (issued internally by hci or
+** vendor lib) in the tx_q.
+**
+** Returns TRUE/FALSE
+**
+*******************************************************************************/
+uint8_t hci_mct_send_int_cmd(uint16_t opcode, HC_BT_HDR *p_buf, \
+ tINT_CMD_CBACK p_cback)
+{
+ if (mct_cb.int_cmd_rsp_pending > INT_CMD_PKT_MAX_COUNT)
+ {
+ ALOGE( \
+ "Allow only %d outstanding internal commands at a time [Reject 0x%04X]"\
+ , INT_CMD_PKT_MAX_COUNT, opcode);
+ return FALSE;
+ }
+
+ mct_cb.int_cmd_rsp_pending++;
+ mct_cb.int_cmd[mct_cb.int_cmd_wrt_idx].opcode = opcode;
+ mct_cb.int_cmd[mct_cb.int_cmd_wrt_idx].cback = p_cback;
+ mct_cb.int_cmd_wrt_idx = ((mct_cb.int_cmd_wrt_idx+1) & INT_CMD_PKT_IDX_MASK);
+
+ /* stamp signature to indicate an internal command */
+ p_buf->layer_specific = opcode;
+
+ utils_enqueue(&tx_q, (void *) p_buf);
+ bthc_signal_event(HC_EVENT_TX);
+
+ return TRUE;
+}
+
+
+/*******************************************************************************
+**
+** Function hci_mct_get_acl_data_length
+**
+** Description Issue HCI_READ_BUFFER_SIZE command to retrieve Controller's
+** ACL data length setting
+**
+** Returns None
+**
+*******************************************************************************/
+void hci_mct_get_acl_data_length(void)
+{
+ HC_BT_HDR *p_buf = NULL;
+ uint8_t *p, ret;
+
+ if (bt_hc_cbacks)
+ {
+ p_buf = (HC_BT_HDR *) bt_hc_cbacks->alloc(BT_HC_HDR_SIZE + \
+ HCI_CMD_PREAMBLE_SIZE);
+ }
+
+ if (p_buf)
+ {
+ p_buf->event = MSG_STACK_TO_HC_HCI_CMD;
+ p_buf->offset = 0;
+ p_buf->layer_specific = 0;
+ p_buf->len = HCI_CMD_PREAMBLE_SIZE;
+
+ p = (uint8_t *) (p_buf + 1);
+ UINT16_TO_STREAM(p, HCI_READ_BUFFER_SIZE);
+ *p = 0;
+
+ if ((ret = hci_mct_send_int_cmd(HCI_READ_BUFFER_SIZE, p_buf, \
+ get_acl_data_length_cback)) == FALSE)
+ {
+ bt_hc_cbacks->dealloc((TRANSAC) p_buf, (char *) (p_buf + 1));
+ }
+ else
+ return;
+ }
+
+ if (bt_hc_cbacks)
+ {
+ ALOGE("hci lib postload aborted");
+ bt_hc_cbacks->postload_cb(NULL, BT_HC_POSTLOAD_FAIL);
+ }
+}
+
+
+/******************************************************************************
+** HCI MCT Services interface table
+******************************************************************************/
+
+const tHCI_IF hci_mct_func_table =
+{
+ hci_mct_init,
+ hci_mct_cleanup,
+ hci_mct_send_msg,
+ hci_mct_send_int_cmd,
+ hci_mct_get_acl_data_length,
+ hci_mct_receive_evt_msg,
+ hci_mct_receive_acl_msg
+};
+
+
diff --git a/hci/src/lpm.c b/hci/src/lpm.c
new file mode 100644
index 0000000..fb6f837
--- /dev/null
+++ b/hci/src/lpm.c
@@ -0,0 +1,425 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2009-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * Filename: lpm.c
+ *
+ * Description: Contains low power mode implementation
+ *
+ ******************************************************************************/
+
+#define LOG_TAG "bt_lpm"
+
+#include <utils/Log.h>
+#include <signal.h>
+#include <time.h>
+#include "bt_hci_bdroid.h"
+#include "bt_vendor_lib.h"
+
+/******************************************************************************
+** Constants & Macros
+******************************************************************************/
+
+#ifndef BTLPM_DBG
+#define BTLPM_DBG FALSE
+#endif
+
+#if (BTLPM_DBG == TRUE)
+#define BTLPMDBG(param, ...) {ALOGD(param, ## __VA_ARGS__);}
+#else
+#define BTLPMDBG(param, ...) {}
+#endif
+
+#ifndef DEFAULT_LPM_IDLE_TIMEOUT
+#define DEFAULT_LPM_IDLE_TIMEOUT 3000
+#endif
+
+/******************************************************************************
+** Externs
+******************************************************************************/
+
+extern bt_vendor_interface_t *bt_vnd_if;
+
+/******************************************************************************
+** Local type definitions
+******************************************************************************/
+
+/* Low power mode state */
+enum {
+ LPM_DISABLED = 0, /* initial state */
+ LPM_ENABLED,
+ LPM_ENABLING,
+ LPM_DISABLING
+};
+
+/* LPM WAKE state */
+enum {
+ LPM_WAKE_DEASSERTED = 0, /* initial state */
+ LPM_WAKE_W4_TX_DONE,
+ LPM_WAKE_W4_TIMEOUT,
+ LPM_WAKE_ASSERTED
+};
+
+/* low power mode control block */
+typedef struct
+{
+ uint8_t state; /* Low power mode state */
+ uint8_t wake_state; /* LPM WAKE state */
+ uint8_t no_tx_data;
+ uint8_t timer_created;
+ timer_t timer_id;
+ uint32_t timeout_ms;
+} bt_lpm_cb_t;
+
+
+/******************************************************************************
+** Static variables
+******************************************************************************/
+
+static bt_lpm_cb_t bt_lpm_cb;
+
+/******************************************************************************
+** LPM Static Functions
+******************************************************************************/
+
+/*******************************************************************************
+**
+** Function lpm_idle_timeout
+**
+** Description Timeout thread of transport idle timer
+**
+** Returns None
+**
+*******************************************************************************/
+static void lpm_idle_timeout(union sigval arg)
+{
+ BTLPMDBG("..lpm_idle_timeout..");
+
+ if ((bt_lpm_cb.state == LPM_ENABLED) && \
+ (bt_lpm_cb.wake_state == LPM_WAKE_W4_TIMEOUT))
+ {
+ bthc_signal_event(HC_EVENT_LPM_IDLE_TIMEOUT);
+ }
+}
+
+/*******************************************************************************
+**
+** Function lpm_start_transport_idle_timer
+**
+** Description Launch transport idle timer
+**
+** Returns None
+**
+*******************************************************************************/
+static void lpm_start_transport_idle_timer(void)
+{
+ int status;
+ struct itimerspec ts;
+ struct sigevent se;
+
+ if (bt_lpm_cb.state != LPM_ENABLED)
+ return;
+
+ if (bt_lpm_cb.timer_created == FALSE)
+ {
+ se.sigev_notify = SIGEV_THREAD;
+ se.sigev_value.sival_ptr = &bt_lpm_cb.timer_id;
+ se.sigev_notify_function = lpm_idle_timeout;
+ se.sigev_notify_attributes = NULL;
+
+ status = timer_create(CLOCK_MONOTONIC, &se, &bt_lpm_cb.timer_id);
+
+ if (status == 0)
+ bt_lpm_cb.timer_created = TRUE;
+ }
+
+ if (bt_lpm_cb.timer_created == TRUE)
+ {
+ ts.it_value.tv_sec = bt_lpm_cb.timeout_ms/1000;
+ ts.it_value.tv_nsec = 1000*(bt_lpm_cb.timeout_ms%1000);
+ ts.it_interval.tv_sec = 0;
+ ts.it_interval.tv_nsec = 0;
+
+ status = timer_settime(bt_lpm_cb.timer_id, 0, &ts, 0);
+ if (status == -1)
+ ALOGE("[START] Failed to set LPM idle timeout");
+ }
+}
+
+/*******************************************************************************
+**
+** Function lpm_stop_transport_idle_timer
+**
+** Description Launch transport idle timer
+**
+** Returns None
+**
+*******************************************************************************/
+static void lpm_stop_transport_idle_timer(void)
+{
+ int status;
+ struct itimerspec ts;
+
+ if (bt_lpm_cb.timer_created == TRUE)
+ {
+ ts.it_value.tv_sec = 0;
+ ts.it_value.tv_nsec = 0;
+ ts.it_interval.tv_sec = 0;
+ ts.it_interval.tv_nsec = 0;
+
+ status = timer_settime(bt_lpm_cb.timer_id, 0, &ts, 0);
+ if (status == -1)
+ ALOGE("[STOP] Failed to set LPM idle timeout");
+ }
+}
+
+/*******************************************************************************
+**
+** Function lpm_vnd_cback
+**
+** Description Callback of vendor specific result for lpm enable/disable
+** rquest
+**
+** Returns None
+**
+*******************************************************************************/
+void lpm_vnd_cback(uint8_t vnd_result)
+{
+ if (vnd_result == 0)
+ {
+ /* Status == Success */
+ bt_lpm_cb.state = (bt_lpm_cb.state == LPM_ENABLING) ? \
+ LPM_ENABLED : LPM_DISABLED;
+ }
+ else
+ {
+ bt_lpm_cb.state = (bt_lpm_cb.state == LPM_ENABLING) ? \
+ LPM_DISABLED : LPM_ENABLED;
+ }
+
+ if (bt_hc_cbacks)
+ {
+ if (bt_lpm_cb.state == LPM_ENABLED)
+ bt_hc_cbacks->lpm_cb(BT_HC_LPM_ENABLED);
+ else
+ bt_hc_cbacks->lpm_cb(BT_HC_LPM_DISABLED);
+ }
+
+ if (bt_lpm_cb.state == LPM_DISABLED)
+ {
+ if (bt_lpm_cb.timer_created == TRUE)
+ {
+ timer_delete(bt_lpm_cb.timer_id);
+ }
+
+ memset(&bt_lpm_cb, 0, sizeof(bt_lpm_cb_t));
+ }
+}
+
+
+/*****************************************************************************
+** Low Power Mode Interface Functions
+*****************************************************************************/
+
+/*******************************************************************************
+**
+** Function lpm_init
+**
+** Description Init LPM
+**
+** Returns None
+**
+*******************************************************************************/
+void lpm_init(void)
+{
+ memset(&bt_lpm_cb, 0, sizeof(bt_lpm_cb_t));
+
+ /* Calling vendor-specific part */
+ if (bt_vnd_if)
+ bt_vnd_if->op(BT_VND_OP_GET_LPM_IDLE_TIMEOUT, &(bt_lpm_cb.timeout_ms));
+ else
+ bt_lpm_cb.timeout_ms = DEFAULT_LPM_IDLE_TIMEOUT;
+}
+
+/*******************************************************************************
+**
+** Function lpm_cleanup
+**
+** Description Clean up
+**
+** Returns None
+**
+*******************************************************************************/
+void lpm_cleanup(void)
+{
+ if (bt_lpm_cb.timer_created == TRUE)
+ {
+ timer_delete(bt_lpm_cb.timer_id);
+ }
+}
+
+/*******************************************************************************
+**
+** Function lpm_enable
+**
+** Description Enalbe/Disable LPM
+**
+** Returns None
+**
+*******************************************************************************/
+void lpm_enable(uint8_t turn_on)
+{
+ if ((bt_lpm_cb.state!=LPM_DISABLED) && (bt_lpm_cb.state!=LPM_ENABLED))
+ {
+ ALOGW("Still busy on processing prior LPM enable/disable request...");
+ return;
+ }
+
+ if ((turn_on == TRUE) && (bt_lpm_cb.state == LPM_ENABLED))
+ {
+ ALOGI("LPM is already on!!!");
+ if (bt_hc_cbacks)
+ bt_hc_cbacks->lpm_cb(BT_HC_LPM_ENABLED);
+ }
+ else if ((turn_on == FALSE) && (bt_lpm_cb.state == LPM_DISABLED))
+ {
+ ALOGI("LPM is already off!!!");
+ if (bt_hc_cbacks)
+ bt_hc_cbacks->lpm_cb(BT_HC_LPM_DISABLED);
+ }
+
+ /* Calling vendor-specific part */
+ if (bt_vnd_if)
+ {
+ uint8_t lpm_cmd = (turn_on) ? BT_VND_LPM_ENABLE : BT_VND_LPM_DISABLE;
+ bt_lpm_cb.state = (turn_on) ? LPM_ENABLING : LPM_DISABLING;
+ bt_vnd_if->op(BT_VND_OP_LPM_SET_MODE, &lpm_cmd);
+ }
+}
+
+/*******************************************************************************
+**
+** Function lpm_tx_done
+**
+** Description This function is to inform the lpm module
+** if data is waiting in the Tx Q or not.
+**
+** IsTxDone: TRUE if All data in the Tx Q are gone
+** FALSE if any data is still in the Tx Q.
+** Typicaly this function must be called
+** before USERIAL Write and in the Tx Done routine
+**
+** Returns None
+**
+*******************************************************************************/
+void lpm_tx_done(uint8_t is_tx_done)
+{
+ bt_lpm_cb.no_tx_data = is_tx_done;
+
+ if ((bt_lpm_cb.wake_state==LPM_WAKE_W4_TX_DONE) && (is_tx_done==TRUE))
+ {
+ bt_lpm_cb.wake_state = LPM_WAKE_W4_TIMEOUT;
+ lpm_start_transport_idle_timer();
+ }
+}
+
+/*******************************************************************************
+**
+** Function lpm_wake_assert
+**
+** Description Called to wake up Bluetooth chip.
+** Normally this is called when there is data to be sent
+** over UART.
+**
+** Returns TRUE/FALSE
+**
+*******************************************************************************/
+void lpm_wake_assert(void)
+{
+ if (bt_lpm_cb.state != LPM_DISABLED)
+ {
+ BTLPMDBG("LPM WAKE assert");
+
+ /* Calling vendor-specific part */
+ if (bt_vnd_if)
+ {
+ uint8_t state = BT_VND_LPM_WAKE_ASSERT;
+ bt_vnd_if->op(BT_VND_OP_LPM_WAKE_SET_STATE, &state);
+ }
+
+ lpm_stop_transport_idle_timer();
+
+ bt_lpm_cb.wake_state = LPM_WAKE_ASSERTED;
+ }
+
+ lpm_tx_done(FALSE);
+}
+
+/*******************************************************************************
+**
+** Function lpm_allow_bt_device_sleep
+**
+** Description Start LPM idle timer if allowed
+**
+** Returns None
+**
+*******************************************************************************/
+void lpm_allow_bt_device_sleep(void)
+{
+ if ((bt_lpm_cb.state == LPM_ENABLED) && \
+ (bt_lpm_cb.wake_state == LPM_WAKE_ASSERTED))
+ {
+ if(bt_lpm_cb.no_tx_data == TRUE)
+ {
+ bt_lpm_cb.wake_state = LPM_WAKE_W4_TIMEOUT;
+ lpm_start_transport_idle_timer();
+ }
+ else
+ {
+ bt_lpm_cb.wake_state = LPM_WAKE_W4_TX_DONE;
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function lpm_wake_deassert
+**
+** Description Deassert wake if allowed
+**
+** Returns None
+**
+*******************************************************************************/
+void lpm_wake_deassert(void)
+{
+ if ((bt_lpm_cb.state == LPM_ENABLED) && (bt_lpm_cb.no_tx_data == TRUE))
+ {
+ BTLPMDBG("LPM WAKE deassert");
+
+ /* Calling vendor-specific part */
+ if (bt_vnd_if)
+ {
+ uint8_t state = BT_VND_LPM_WAKE_DEASSERT;
+ bt_vnd_if->op(BT_VND_OP_LPM_WAKE_SET_STATE, &state);
+ }
+
+ bt_lpm_cb.wake_state = LPM_WAKE_DEASSERTED;
+ }
+}
+
diff --git a/hci/src/userial.c b/hci/src/userial.c
new file mode 100644
index 0000000..afd3010
--- /dev/null
+++ b/hci/src/userial.c
@@ -0,0 +1,530 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2009-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * Filename: userial.c
+ *
+ * Description: Contains open/read/write/close functions on serial port
+ *
+ ******************************************************************************/
+
+#define LOG_TAG "bt_userial"
+
+#include <utils/Log.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include "bt_hci_bdroid.h"
+#include "userial.h"
+#include "utils.h"
+#include "bt_vendor_lib.h"
+#include <sys/prctl.h>
+
+/******************************************************************************
+** Constants & Macros
+******************************************************************************/
+
+#ifndef USERIAL_DBG
+#define USERIAL_DBG FALSE
+#endif
+
+#if (USERIAL_DBG == TRUE)
+#define USERIALDBG(param, ...) {ALOGD(param, ## __VA_ARGS__);}
+#else
+#define USERIALDBG(param, ...) {}
+#endif
+
+#define MAX_SERIAL_PORT (USERIAL_PORT_3 + 1)
+#define READ_LIMIT (BTHC_USERIAL_READ_MEM_SIZE - BT_HC_HDR_SIZE)
+
+enum {
+ USERIAL_RX_EXIT,
+ USERIAL_RX_FLOW_OFF,
+ USERIAL_RX_FLOW_ON
+};
+
+/******************************************************************************
+** Externs
+******************************************************************************/
+
+extern bt_vendor_interface_t *bt_vnd_if;
+
+/******************************************************************************
+** Local type definitions
+******************************************************************************/
+
+typedef struct
+{
+ int fd;
+ uint8_t port;
+ pthread_t read_thread;
+ BUFFER_Q rx_q;
+ HC_BT_HDR *p_rx_hdr;
+} tUSERIAL_CB;
+
+/******************************************************************************
+** Static variables
+******************************************************************************/
+
+static tUSERIAL_CB userial_cb;
+static volatile uint8_t userial_running = 0;
+
+/******************************************************************************
+** Static functions
+******************************************************************************/
+
+/*****************************************************************************
+** Socket signal functions to wake up userial_read_thread for termination
+**
+** creating an unnamed pair of connected sockets
+** - signal_fds[0]: join fd_set in select call of userial_read_thread
+** - signal_fds[1]: trigger from userial_close
+*****************************************************************************/
+static int signal_fds[2]={0,1};
+static uint8_t rx_flow_on = TRUE;
+static inline int create_signal_fds(fd_set* set)
+{
+ if(signal_fds[0]==0 && socketpair(AF_UNIX, SOCK_STREAM, 0, signal_fds)<0)
+ {
+ ALOGE("create_signal_sockets:socketpair failed, errno: %d", errno);
+ return -1;
+ }
+ FD_SET(signal_fds[0], set);
+ return signal_fds[0];
+}
+static inline int send_wakeup_signal(char sig_cmd)
+{
+ return send(signal_fds[1], &sig_cmd, sizeof(sig_cmd), 0);
+}
+static inline char reset_signal()
+{
+ char sig_recv = -1;
+ recv(signal_fds[0], &sig_recv, sizeof(sig_recv), MSG_WAITALL);
+ return sig_recv;
+}
+static inline int is_signaled(fd_set* set)
+{
+ return FD_ISSET(signal_fds[0], set);
+}
+
+/*******************************************************************************
+**
+** Function select_read
+**
+** Description check if fd is ready for reading and listen for termination
+** signal. need to use select in order to avoid collision
+** between read and close on the same fd
+**
+** Returns -1: termination
+** >=0: numbers of bytes read back from fd
+**
+*******************************************************************************/
+static int select_read(int fd, uint8_t *pbuf, int len)
+{
+ fd_set input;
+ int n = 0, ret = -1;
+ char reason = 0;
+
+ while (userial_running)
+ {
+ /* Initialize the input fd set */
+ FD_ZERO(&input);
+ if (rx_flow_on == TRUE)
+ {
+ FD_SET(fd, &input);
+ }
+ int fd_max = create_signal_fds(&input);
+ fd_max = fd_max > fd ? fd_max : fd;
+
+ /* Do the select */
+ n = select(fd_max+1, &input, NULL, NULL, NULL);
+ if(is_signaled(&input))
+ {
+ reason = reset_signal();
+ if (reason == USERIAL_RX_EXIT)
+ {
+ USERIALDBG("RX termination");
+ return -1;
+ }
+ else if (reason == USERIAL_RX_FLOW_OFF)
+ {
+ USERIALDBG("RX flow OFF");
+ rx_flow_on = FALSE;
+ }
+ else if (reason == USERIAL_RX_FLOW_ON)
+ {
+ USERIALDBG("RX flow ON");
+ rx_flow_on = TRUE;
+ }
+ }
+
+ if (n > 0)
+ {
+ /* We might have input */
+ if (FD_ISSET(fd, &input))
+ {
+ ret = read(fd, pbuf, (size_t)len);
+ if (0 == ret)
+ ALOGW( "read() returned 0!" );
+
+ return ret;
+ }
+ }
+ else if (n < 0)
+ ALOGW( "select() Failed");
+ else if (n == 0)
+ ALOGW( "Got a select() TIMEOUT");
+
+ }
+
+ return ret;
+}
+
+/*******************************************************************************
+**
+** Function userial_read_thread
+**
+** Description
+**
+** Returns void *
+**
+*******************************************************************************/
+static void *userial_read_thread(void *arg)
+{
+ int rx_length = 0;
+ HC_BT_HDR *p_buf = NULL;
+ uint8_t *p;
+
+ USERIALDBG("Entering userial_read_thread()");
+ prctl(PR_SET_NAME, (unsigned long)"userial_read", 0, 0, 0);
+
+ rx_flow_on = TRUE;
+ userial_running = 1;
+
+ while (userial_running)
+ {
+ if (bt_hc_cbacks)
+ {
+ p_buf = (HC_BT_HDR *) bt_hc_cbacks->alloc( \
+ BTHC_USERIAL_READ_MEM_SIZE);
+ }
+ else
+ p_buf = NULL;
+
+ if (p_buf != NULL)
+ {
+ p_buf->offset = 0;
+ p_buf->layer_specific = 0;
+
+ p = (uint8_t *) (p_buf + 1);
+ rx_length = select_read(userial_cb.fd, p, READ_LIMIT);
+ }
+ else
+ {
+ rx_length = 0;
+ utils_delay(100);
+ ALOGW("userial_read_thread() failed to gain buffers");
+ continue;
+ }
+
+
+ if (rx_length > 0)
+ {
+ p_buf->len = (uint16_t)rx_length;
+ utils_enqueue(&(userial_cb.rx_q), p_buf);
+ bthc_signal_event(HC_EVENT_RX);
+ }
+ else /* either 0 or < 0 */
+ {
+ ALOGW("select_read return size <=0:%d, exiting userial_read_thread",\
+ rx_length);
+ /* if we get here, we should have a buffer */
+ bt_hc_cbacks->dealloc((TRANSAC) p_buf, (char *) (p_buf + 1));
+ /* negative value means exit thread */
+ break;
+ }
+ } /* for */
+
+ userial_running = 0;
+ USERIALDBG("Leaving userial_read_thread()");
+ pthread_exit(NULL);
+
+ return NULL; // Compiler friendly
+}
+
+
+/*****************************************************************************
+** Userial API Functions
+*****************************************************************************/
+
+/*******************************************************************************
+**
+** Function userial_init
+**
+** Description Initializes the userial driver
+**
+** Returns TRUE/FALSE
+**
+*******************************************************************************/
+uint8_t userial_init(void)
+{
+ USERIALDBG("userial_init");
+ memset(&userial_cb, 0, sizeof(tUSERIAL_CB));
+ userial_cb.fd = -1;
+ utils_queue_init(&(userial_cb.rx_q));
+ return TRUE;
+}
+
+
+/*******************************************************************************
+**
+** Function userial_open
+**
+** Description Open Bluetooth device with the port ID
+**
+** Returns TRUE/FALSE
+**
+*******************************************************************************/
+uint8_t userial_open(uint8_t port)
+{
+ struct sched_param param;
+ int policy, result;
+ pthread_attr_t thread_attr;
+ int fd_array[CH_MAX];
+
+ USERIALDBG("userial_open(port:%d)", port);
+
+ if (userial_running)
+ {
+ /* Userial is open; close it first */
+ userial_close();
+ utils_delay(50);
+ }
+
+ if (port >= MAX_SERIAL_PORT)
+ {
+ ALOGE("Port > MAX_SERIAL_PORT");
+ return FALSE;
+ }
+
+ /* Calling vendor-specific part */
+ if (bt_vnd_if)
+ {
+ result = bt_vnd_if->op(BT_VND_OP_USERIAL_OPEN, &fd_array);
+
+ if (result != 1)
+ {
+ ALOGE("userial_open: wrong numbers of open fd in vendor lib [%d]!",
+ result);
+ ALOGE("userial_open: HCI UART expects only one open fd");
+ bt_vnd_if->op(BT_VND_OP_USERIAL_CLOSE, NULL);
+ return FALSE;
+ }
+
+ userial_cb.fd = fd_array[0];
+ }
+ else
+ {
+ ALOGE("userial_open: missing vendor lib interface !!!");
+ ALOGE("userial_open: unable to open UART port");
+ return FALSE;
+ }
+
+ if (userial_cb.fd == -1)
+ {
+ ALOGE("userial_open: failed to open UART port");
+ return FALSE;
+ }
+
+ USERIALDBG( "fd = %d", userial_cb.fd);
+
+ userial_cb.port = port;
+
+ pthread_attr_init(&thread_attr);
+
+ if (pthread_create(&(userial_cb.read_thread), &thread_attr, \
+ userial_read_thread, NULL) != 0 )
+ {
+ ALOGE("pthread_create failed!");
+ return FALSE;
+ }
+
+ if(pthread_getschedparam(userial_cb.read_thread, &policy, &param)==0)
+ {
+ policy = BTHC_LINUX_BASE_POLICY;
+#if (BTHC_LINUX_BASE_POLICY!=SCHED_NORMAL)
+ param.sched_priority = BTHC_USERIAL_READ_THREAD_PRIORITY;
+#endif
+ result = pthread_setschedparam(userial_cb.read_thread, policy, &param);
+ if (result != 0)
+ {
+ ALOGW("userial_open: pthread_setschedparam failed (%s)", \
+ strerror(result));
+ }
+ }
+
+ return TRUE;
+}
+
+/*******************************************************************************
+**
+** Function userial_read
+**
+** Description Read data from the userial port
+**
+** Returns Number of bytes actually read from the userial port and
+** copied into p_data. This may be less than len.
+**
+*******************************************************************************/
+uint16_t userial_read(uint16_t msg_id, uint8_t *p_buffer, uint16_t len)
+{
+ uint16_t total_len = 0;
+ uint16_t copy_len = 0;
+ uint8_t *p_data = NULL;
+
+ do
+ {
+ if(userial_cb.p_rx_hdr != NULL)
+ {
+ p_data = ((uint8_t *)(userial_cb.p_rx_hdr + 1)) + \
+ (userial_cb.p_rx_hdr->offset);
+
+ if((userial_cb.p_rx_hdr->len) <= (len - total_len))
+ copy_len = userial_cb.p_rx_hdr->len;
+ else
+ copy_len = (len - total_len);
+
+ memcpy((p_buffer + total_len), p_data, copy_len);
+
+ total_len += copy_len;
+
+ userial_cb.p_rx_hdr->offset += copy_len;
+ userial_cb.p_rx_hdr->len -= copy_len;
+
+ if(userial_cb.p_rx_hdr->len == 0)
+ {
+ if (bt_hc_cbacks)
+ bt_hc_cbacks->dealloc((TRANSAC) userial_cb.p_rx_hdr, \
+ (char *) (userial_cb.p_rx_hdr+1));
+
+ userial_cb.p_rx_hdr = NULL;
+ }
+ }
+
+ if(userial_cb.p_rx_hdr == NULL)
+ {
+ userial_cb.p_rx_hdr=(HC_BT_HDR *)utils_dequeue(&(userial_cb.rx_q));
+ }
+ } while ((userial_cb.p_rx_hdr != NULL) && (total_len < len));
+
+ return total_len;
+}
+
+/*******************************************************************************
+**
+** Function userial_write
+**
+** Description Write data to the userial port
+**
+** Returns Number of bytes actually written to the userial port. This
+** may be less than len.
+**
+*******************************************************************************/
+uint16_t userial_write(uint16_t msg_id, uint8_t *p_data, uint16_t len)
+{
+ int ret, total = 0;
+
+ while(len != 0)
+ {
+ ret = write(userial_cb.fd, p_data+total, len);
+ total += ret;
+ len -= ret;
+ }
+
+ return ((uint16_t)total);
+}
+
+/*******************************************************************************
+**
+** Function userial_close
+**
+** Description Close the userial port
+**
+** Returns None
+**
+*******************************************************************************/
+void userial_close(void)
+{
+ int result;
+ TRANSAC p_buf;
+
+ USERIALDBG("userial_close(fd:%d)", userial_cb.fd);
+
+ if (userial_running)
+ send_wakeup_signal(USERIAL_RX_EXIT);
+
+ if ((result=pthread_join(userial_cb.read_thread, NULL)) < 0)
+ ALOGE( "pthread_join() FAILED result:%d", result);
+
+ /* Calling vendor-specific part */
+ if (bt_vnd_if)
+ bt_vnd_if->op(BT_VND_OP_USERIAL_CLOSE, NULL);
+
+ userial_cb.fd = -1;
+
+ if (bt_hc_cbacks)
+ {
+ while ((p_buf = utils_dequeue (&(userial_cb.rx_q))) != NULL)
+ {
+ bt_hc_cbacks->dealloc(p_buf, (char *) ((HC_BT_HDR *)p_buf+1));
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function userial_ioctl
+**
+** Description ioctl inteface
+**
+** Returns None
+**
+*******************************************************************************/
+void userial_ioctl(userial_ioctl_op_t op, void *p_data)
+{
+ switch(op)
+ {
+ case USERIAL_OP_RXFLOW_ON:
+ if (userial_running)
+ send_wakeup_signal(USERIAL_RX_FLOW_ON);
+ break;
+
+ case USERIAL_OP_RXFLOW_OFF:
+ if (userial_running)
+ send_wakeup_signal(USERIAL_RX_FLOW_OFF);
+ break;
+
+ case USERIAL_OP_INIT:
+ default:
+ break;
+ }
+}
+
diff --git a/hci/src/userial_mct.c b/hci/src/userial_mct.c
new file mode 100644
index 0000000..5c33285
--- /dev/null
+++ b/hci/src/userial_mct.c
@@ -0,0 +1,439 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2009-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * Filename: userial_mct.c
+ *
+ * Description: Contains open/read/write/close functions on multi-channels
+ *
+ ******************************************************************************/
+
+#define LOG_TAG "bt_userial_mct"
+
+#include <utils/Log.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include "bt_hci_bdroid.h"
+#include "userial.h"
+#include "utils.h"
+#include "bt_vendor_lib.h"
+
+/******************************************************************************
+** Constants & Macros
+******************************************************************************/
+
+#define USERIAL_DBG TRUE
+
+#ifndef USERIAL_DBG
+#define USERIAL_DBG FALSE
+#endif
+
+#if (USERIAL_DBG == TRUE)
+#define USERIALDBG(param, ...) {ALOGD(param, ## __VA_ARGS__);}
+#else
+#define USERIALDBG(param, ...) {}
+#endif
+
+#define MAX_SERIAL_PORT (USERIAL_PORT_3 + 1)
+
+enum {
+ USERIAL_RX_EXIT,
+ USERIAL_RX_FLOW_OFF,
+ USERIAL_RX_FLOW_ON
+};
+
+/******************************************************************************
+** Externs
+******************************************************************************/
+
+extern bt_vendor_interface_t *bt_vnd_if;
+uint16_t hci_mct_receive_evt_msg(void);
+uint16_t hci_mct_receive_acl_msg(void);
+
+
+/******************************************************************************
+** Local type definitions
+******************************************************************************/
+
+typedef struct
+{
+ int fd[CH_MAX];
+ uint8_t port;
+ pthread_t read_thread;
+ BUFFER_Q rx_q;
+ HC_BT_HDR *p_rx_hdr;
+} tUSERIAL_CB;
+
+/******************************************************************************
+** Static variables
+******************************************************************************/
+
+static tUSERIAL_CB userial_cb;
+static volatile uint8_t userial_running = 0;
+
+/******************************************************************************
+** Static functions
+******************************************************************************/
+
+/*****************************************************************************
+** Socket signal functions to wake up userial_read_thread for termination
+**
+** creating an unnamed pair of connected sockets
+** - signal_fds[0]: join fd_set in select call of userial_read_thread
+** - signal_fds[1]: trigger from userial_close
+*****************************************************************************/
+static int signal_fds[2]={0,1};
+static uint8_t rx_flow_on = TRUE;
+static inline int create_signal_fds(fd_set* set)
+{
+ if(signal_fds[0]==0 && socketpair(AF_UNIX, SOCK_STREAM, 0, signal_fds)<0)
+ {
+ ALOGE("create_signal_sockets:socketpair failed, errno: %d", errno);
+ return -1;
+ }
+ FD_SET(signal_fds[0], set);
+ return signal_fds[0];
+}
+static inline int send_wakeup_signal(char sig_cmd)
+{
+ return send(signal_fds[1], &sig_cmd, sizeof(sig_cmd), 0);
+}
+static inline char reset_signal()
+{
+ char sig_recv = -1;
+ recv(signal_fds[0], &sig_recv, sizeof(sig_recv), MSG_WAITALL);
+ return sig_recv;
+}
+static inline int is_signaled(fd_set* set)
+{
+ return FD_ISSET(signal_fds[0], set);
+}
+
+/*******************************************************************************
+**
+** Function userial_evt_read_thread
+**
+** Description The reading thread on EVT and ACL_IN channels
+**
+** Returns void *
+**
+*******************************************************************************/
+static void *userial_read_thread(void *arg)
+{
+ fd_set input;
+ int n;
+ char reason = 0;
+
+ USERIALDBG("Entering userial_read_thread()");
+
+ rx_flow_on = TRUE;
+ userial_running = 1;
+
+ while (userial_running)
+ {
+ /* Initialize the input fd set */
+ FD_ZERO(&input);
+ if (rx_flow_on == TRUE)
+ {
+ FD_SET(userial_cb.fd[CH_EVT], &input);
+ FD_SET(userial_cb.fd[CH_ACL_IN], &input);
+ }
+
+ int fd_max = create_signal_fds(&input);
+ fd_max = (fd_max>userial_cb.fd[CH_EVT]) ? fd_max : userial_cb.fd[CH_EVT];
+ fd_max = (fd_max>userial_cb.fd[CH_ACL_IN]) ? fd_max : userial_cb.fd[CH_ACL_IN];
+
+ /* Do the select */
+ n = 0;
+ n = select(fd_max+1, &input, NULL, NULL, NULL);
+ if(is_signaled(&input))
+ {
+ reason = reset_signal();
+ if (reason == USERIAL_RX_EXIT)
+ {
+ ALOGI("exiting userial_read_thread");
+ userial_running = 0;
+ break;
+ }
+ else if (reason == USERIAL_RX_FLOW_OFF)
+ {
+ USERIALDBG("RX flow OFF");
+ rx_flow_on = FALSE;
+ }
+ else if (reason == USERIAL_RX_FLOW_ON)
+ {
+ USERIALDBG("RX flow ON");
+ rx_flow_on = TRUE;
+ }
+ }
+
+ if (n > 0)
+ {
+ /* We might have input */
+ if (FD_ISSET(userial_cb.fd[CH_EVT], &input))
+ {
+ hci_mct_receive_evt_msg();
+ }
+
+ if (FD_ISSET(userial_cb.fd[CH_ACL_IN], &input))
+ {
+ hci_mct_receive_acl_msg();
+ }
+ }
+ else if (n < 0)
+ ALOGW( "select() Failed");
+ else if (n == 0)
+ ALOGW( "Got a select() TIMEOUT");
+ } /* while */
+
+ userial_running = 0;
+ USERIALDBG("Leaving userial_evt_read_thread()");
+ pthread_exit(NULL);
+
+ return NULL; // Compiler friendly
+}
+
+
+/*****************************************************************************
+** Userial API Functions
+*****************************************************************************/
+
+/*******************************************************************************
+**
+** Function userial_init
+**
+** Description Initializes the userial driver
+**
+** Returns TRUE/FALSE
+**
+*******************************************************************************/
+uint8_t userial_init(void)
+{
+ int idx;
+
+ USERIALDBG("userial_init");
+ memset(&userial_cb, 0, sizeof(tUSERIAL_CB));
+ for (idx=0; idx < CH_MAX; idx++)
+ userial_cb.fd[idx] = -1;
+ utils_queue_init(&(userial_cb.rx_q));
+ return TRUE;
+}
+
+
+/*******************************************************************************
+**
+** Function userial_open
+**
+** Description Open Bluetooth device with the port ID
+**
+** Returns TRUE/FALSE
+**
+*******************************************************************************/
+uint8_t userial_open(uint8_t port)
+{
+ struct sched_param param;
+ int policy, result;
+ pthread_attr_t thread_attr;
+
+ USERIALDBG("userial_open(port:%d)", port);
+
+ if (userial_running)
+ {
+ /* Userial is open; close it first */
+ userial_close();
+ utils_delay(50);
+ }
+
+ if (port >= MAX_SERIAL_PORT)
+ {
+ ALOGE("Port > MAX_SERIAL_PORT");
+ return FALSE;
+ }
+
+ /* Calling vendor-specific part */
+ if (bt_vnd_if)
+ {
+ result = bt_vnd_if->op(BT_VND_OP_USERIAL_OPEN, &userial_cb.fd);
+
+ if ((result != 2) && (result != 4))
+ {
+ ALOGE("userial_open: wrong numbers of open fd in vendor lib [%d]!",
+ result);
+ ALOGE("userial_open: HCI MCT expects 2 or 4 open file descriptors");
+ bt_vnd_if->op(BT_VND_OP_USERIAL_CLOSE, NULL);
+ return FALSE;
+ }
+ }
+ else
+ {
+ ALOGE("userial_open: missing vendor lib interface !!!");
+ ALOGE("userial_open: unable to open BT transport");
+ return FALSE;
+ }
+
+ ALOGI("CMD=%d, EVT=%d, ACL_Out=%d, ACL_In=%d", \
+ userial_cb.fd[CH_CMD], userial_cb.fd[CH_EVT], \
+ userial_cb.fd[CH_ACL_OUT], userial_cb.fd[CH_ACL_IN]);
+
+ if ((userial_cb.fd[CH_CMD] == -1) || (userial_cb.fd[CH_EVT] == -1) ||
+ (userial_cb.fd[CH_ACL_OUT] == -1) || (userial_cb.fd[CH_ACL_IN] == -1))
+ {
+ ALOGE("userial_open: failed to open BT transport");
+ bt_vnd_if->op(BT_VND_OP_USERIAL_CLOSE, NULL);
+ return FALSE;
+ }
+
+ userial_cb.port = port;
+
+ /* Start listening thread */
+ pthread_attr_init(&thread_attr);
+
+ if (pthread_create(&(userial_cb.read_thread), &thread_attr, \
+ userial_read_thread, NULL) != 0 )
+ {
+ ALOGE("pthread_create failed!");
+ bt_vnd_if->op(BT_VND_OP_USERIAL_CLOSE, NULL);
+ return FALSE;
+ }
+
+ if(pthread_getschedparam(userial_cb.read_thread, &policy, &param)==0)
+ {
+ policy = BTHC_LINUX_BASE_POLICY;
+#if (BTHC_LINUX_BASE_POLICY!=SCHED_NORMAL)
+ param.sched_priority = BTHC_USERIAL_READ_THREAD_PRIORITY;
+#endif
+ result=pthread_setschedparam(userial_cb.read_thread,policy,&param);
+ if (result != 0)
+ {
+ ALOGW("userial_open: pthread_setschedparam failed (%s)", \
+ strerror(result));
+ }
+ }
+
+ return TRUE;
+}
+
+/*******************************************************************************
+**
+** Function userial_read
+**
+** Description Read data from the userial channel
+**
+** Returns Number of bytes actually read from the userial port and
+** copied into p_data. This may be less than len.
+**
+*******************************************************************************/
+uint16_t userial_read(uint16_t msg_id, uint8_t *p_buffer, uint16_t len)
+{
+ int ret = -1;
+ int ch_idx = (msg_id == MSG_HC_TO_STACK_HCI_EVT) ? CH_EVT : CH_ACL_IN;
+
+ ret = read(userial_cb.fd[ch_idx], p_buffer, (size_t)len);
+ if (ret <= 0)
+ ALOGW( "userial_read: read() returned %d!", ret);
+
+ return (uint16_t) ((ret >= 0) ? ret : 0);
+}
+
+/*******************************************************************************
+**
+** Function userial_write
+**
+** Description Write data to the userial port
+**
+** Returns Number of bytes actually written to the userial port. This
+** may be less than len.
+**
+*******************************************************************************/
+uint16_t userial_write(uint16_t msg_id, uint8_t *p_data, uint16_t len)
+{
+ int ret, total = 0;
+ int ch_idx = (msg_id == MSG_STACK_TO_HC_HCI_CMD) ? CH_CMD : CH_ACL_OUT;
+
+ while(len != 0)
+ {
+ ret = write(userial_cb.fd[ch_idx], p_data+total, len);
+ total += ret;
+ len -= ret;
+ }
+
+ return ((uint16_t)total);
+}
+
+/*******************************************************************************
+**
+** Function userial_close
+**
+** Description Close the userial port
+**
+** Returns None
+**
+*******************************************************************************/
+void userial_close(void)
+{
+ int idx, result;
+
+ USERIALDBG("userial_close");
+
+ if (userial_running)
+ send_wakeup_signal(USERIAL_RX_EXIT);
+
+ if ((result=pthread_join(userial_cb.read_thread, NULL)) < 0)
+ ALOGE( "pthread_join() FAILED result:%d", result);
+
+ /* Calling vendor-specific part */
+ if (bt_vnd_if)
+ bt_vnd_if->op(BT_VND_OP_USERIAL_CLOSE, NULL);
+
+ for (idx=0; idx < CH_MAX; idx++)
+ userial_cb.fd[idx] = -1;
+}
+
+/*******************************************************************************
+**
+** Function userial_ioctl
+**
+** Description ioctl inteface
+**
+** Returns None
+**
+*******************************************************************************/
+void userial_ioctl(userial_ioctl_op_t op, void *p_data)
+{
+ switch(op)
+ {
+ case USERIAL_OP_RXFLOW_ON:
+ if (userial_running)
+ send_wakeup_signal(USERIAL_RX_FLOW_ON);
+ break;
+
+ case USERIAL_OP_RXFLOW_OFF:
+ if (userial_running)
+ send_wakeup_signal(USERIAL_RX_FLOW_OFF);
+ break;
+
+ case USERIAL_OP_INIT:
+ default:
+ break;
+ }
+}
+
diff --git a/hci/src/utils.c b/hci/src/utils.c
new file mode 100644
index 0000000..bfcf724
--- /dev/null
+++ b/hci/src/utils.c
@@ -0,0 +1,308 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2009-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * Filename: utils.c
+ *
+ * Description: Contains helper functions
+ *
+ ******************************************************************************/
+
+#include <errno.h>
+#include <pthread.h>
+#include <time.h>
+#include "bt_hci_bdroid.h"
+#include "utils.h"
+
+/******************************************************************************
+** Static variables
+******************************************************************************/
+
+static pthread_mutex_t utils_mutex;
+
+/*****************************************************************************
+** UTILS INTERFACE FUNCTIONS
+*****************************************************************************/
+
+/*******************************************************************************
+**
+** Function utils_init
+**
+** Description Utils initialization
+**
+** Returns None
+**
+*******************************************************************************/
+void utils_init (void)
+{
+ pthread_mutex_init(&utils_mutex, NULL);
+}
+
+/*******************************************************************************
+**
+** Function utils_cleanup
+**
+** Description Utils cleanup
+**
+** Returns None
+**
+*******************************************************************************/
+void utils_cleanup (void)
+{
+}
+
+/*******************************************************************************
+**
+** Function utils_queue_init
+**
+** Description Initialize the given buffer queue
+**
+** Returns None
+**
+*******************************************************************************/
+void utils_queue_init (BUFFER_Q *p_q)
+{
+ p_q->p_first = p_q->p_last = NULL;
+ p_q->count = 0;
+}
+
+/*******************************************************************************
+**
+** Function utils_enqueue
+**
+** Description Enqueue a buffer at the tail of the given queue
+**
+** Returns None
+**
+*******************************************************************************/
+void utils_enqueue (BUFFER_Q *p_q, void *p_buf)
+{
+ HC_BUFFER_HDR_T *p_hdr;
+
+ p_hdr = (HC_BUFFER_HDR_T *) ((uint8_t *) p_buf - BT_HC_BUFFER_HDR_SIZE);
+
+ pthread_mutex_lock(&utils_mutex);
+
+ if (p_q->p_last)
+ {
+ HC_BUFFER_HDR_T *p_last_hdr = \
+ (HC_BUFFER_HDR_T *)((uint8_t *)p_q->p_last - BT_HC_BUFFER_HDR_SIZE);
+
+ p_last_hdr->p_next = p_hdr;
+ }
+ else
+ p_q->p_first = p_buf;
+
+ p_q->p_last = p_buf;
+ p_q->count++;
+
+ p_hdr->p_next = NULL;
+
+ pthread_mutex_unlock(&utils_mutex);
+}
+/*******************************************************************************
+**
+** Function utils_dequeue
+**
+** Description Dequeues a buffer from the head of the given queue
+**
+** Returns NULL if queue is empty, else buffer
+**
+*******************************************************************************/
+void *utils_dequeue (BUFFER_Q *p_q)
+{
+ pthread_mutex_lock(&utils_mutex);
+ void* p_buf = utils_dequeue_unlocked(p_q);
+ pthread_mutex_unlock(&utils_mutex);
+ return p_buf;
+}
+
+/*******************************************************************************
+**
+** Function utils_dequeue_unlocked
+**
+** Description Dequeues a buffer from the head of the given queue without lock
+**
+** Returns NULL if queue is empty, else buffer
+**
+*******************************************************************************/
+void *utils_dequeue_unlocked (BUFFER_Q *p_q)
+{
+ HC_BUFFER_HDR_T *p_hdr;
+
+
+ if (!p_q || !p_q->count)
+ {
+ return (NULL);
+ }
+
+ p_hdr=(HC_BUFFER_HDR_T *)((uint8_t *)p_q->p_first-BT_HC_BUFFER_HDR_SIZE);
+
+ if (p_hdr->p_next)
+ p_q->p_first = ((uint8_t *)p_hdr->p_next + BT_HC_BUFFER_HDR_SIZE);
+ else
+ {
+ p_q->p_first = NULL;
+ p_q->p_last = NULL;
+ }
+
+ p_q->count--;
+
+ p_hdr->p_next = NULL;
+ return ((uint8_t *)p_hdr + BT_HC_BUFFER_HDR_SIZE);
+}
+
+/*******************************************************************************
+**
+** Function utils_getnext
+**
+** Description Return a pointer to the next buffer linked to the given
+** buffer
+**
+** Returns NULL if the given buffer does not point to any next buffer,
+** else next buffer address
+**
+*******************************************************************************/
+void *utils_getnext (void *p_buf)
+{
+ HC_BUFFER_HDR_T *p_hdr;
+
+ p_hdr = (HC_BUFFER_HDR_T *) ((uint8_t *) p_buf - BT_HC_BUFFER_HDR_SIZE);
+
+ if (p_hdr->p_next)
+ return ((uint8_t *)p_hdr->p_next + BT_HC_BUFFER_HDR_SIZE);
+ else
+ return (NULL);
+}
+
+/*******************************************************************************
+**
+** Function utils_remove_from_queue
+**
+** Description Dequeue the given buffer from the middle of the given queue
+**
+** Returns NULL if the given queue is empty, else the given buffer
+**
+*******************************************************************************/
+void *utils_remove_from_queue (BUFFER_Q *p_q, void *p_buf)
+{
+ pthread_mutex_lock(&utils_mutex);
+ p_buf = utils_remove_from_queue_unlocked(p_q, p_buf);
+ pthread_mutex_unlock(&utils_mutex);
+ return p_buf;
+}
+/*******************************************************************************
+**
+** Function utils_remove_from_queue_unlocked
+**
+** Description Dequeue the given buffer from the middle of the given queue
+**
+** Returns NULL if the given queue is empty, else the given buffer
+**
+*******************************************************************************/
+void *utils_remove_from_queue_unlocked (BUFFER_Q *p_q, void *p_buf)
+{
+ HC_BUFFER_HDR_T *p_prev;
+ HC_BUFFER_HDR_T *p_buf_hdr;
+
+
+ if (p_buf == p_q->p_first)
+ {
+ return (utils_dequeue_unlocked (p_q));
+ }
+
+ p_buf_hdr = (HC_BUFFER_HDR_T *)((uint8_t *)p_buf - BT_HC_BUFFER_HDR_SIZE);
+ p_prev=(HC_BUFFER_HDR_T *)((uint8_t *)p_q->p_first-BT_HC_BUFFER_HDR_SIZE);
+
+ for ( ; p_prev; p_prev = p_prev->p_next)
+ {
+ /* If the previous points to this one, move the pointers around */
+ if (p_prev->p_next == p_buf_hdr)
+ {
+ p_prev->p_next = p_buf_hdr->p_next;
+
+ /* If we are removing the last guy in the queue, update p_last */
+ if (p_buf == p_q->p_last)
+ p_q->p_last = p_prev + 1;
+
+ /* One less in the queue */
+ p_q->count--;
+
+ /* The buffer is now unlinked */
+ p_buf_hdr->p_next = NULL;
+
+ return (p_buf);
+ }
+ }
+ return (NULL);
+}
+
+/*******************************************************************************
+**
+** Function utils_delay
+**
+** Description sleep unconditionally for timeout milliseconds
+**
+** Returns None
+**
+*******************************************************************************/
+void utils_delay (uint32_t timeout)
+{
+ struct timespec delay;
+ int err;
+
+ delay.tv_sec = timeout / 1000;
+ delay.tv_nsec = 1000 * 1000 * (timeout%1000);
+
+ /* [u]sleep can't be used because it uses SIGALRM */
+ do {
+ err = nanosleep(&delay, &delay);
+ } while (err < 0 && errno ==EINTR);
+}
+
+/*******************************************************************************
+**
+** Function utils_lock
+**
+** Description application calls this function before entering critical
+** section
+**
+** Returns None
+**
+*******************************************************************************/
+void utils_lock (void)
+{
+ pthread_mutex_lock(&utils_mutex);
+}
+
+/*******************************************************************************
+**
+** Function utils_unlock
+**
+** Description application calls this function when leaving critical
+** section
+**
+** Returns None
+**
+*******************************************************************************/
+void utils_unlock (void)
+{
+ pthread_mutex_unlock(&utils_mutex);
+}
+