summaryrefslogtreecommitdiffstats
path: root/bt-vendor
diff options
context:
space:
mode:
Diffstat (limited to 'bt-vendor')
-rw-r--r--bt-vendor/Android.mk30
-rw-r--r--bt-vendor/gta04_bt_vendor.c429
-rw-r--r--bt-vendor/gta04_bt_vendor.h104
-rw-r--r--bt-vendor/hci.c359
4 files changed, 922 insertions, 0 deletions
diff --git a/bt-vendor/Android.mk b/bt-vendor/Android.mk
new file mode 100644
index 0000000..10350b6
--- /dev/null
+++ b/bt-vendor/Android.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 2014 Paul Kocialkowski <contact@paulk.fr>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := gta04_bt_vendor.c hci.c
+
+LOCAL_C_INCLUDES := external/bluetooth/bluedroid/hci/include
+
+LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_MODULE := libbt-vendor
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/bt-vendor/gta04_bt_vendor.c b/bt-vendor/gta04_bt_vendor.c
new file mode 100644
index 0000000..8b49bb5
--- /dev/null
+++ b/bt-vendor/gta04_bt_vendor.c
@@ -0,0 +1,429 @@
+/*
+ * Copyright (C) 2014 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <termios.h>
+
+#define LOG_TAG "gta04_bt_vendor"
+#include <cutils/log.h>
+
+#include <bt_vendor_lib.h>
+#include <bt_hci_bdroid.h>
+
+#include "gta04_bt_vendor.h"
+
+/*
+ * Globals
+ */
+
+const char serial_path[] = "/dev/ttyO0";
+const speed_t serial_init_speed = B3000000;
+const speed_t serial_work_speed = B3000000;
+
+struct gta04_bt_vendor *gta04_bt_vendor = NULL;
+
+/*
+ * Serial
+ */
+
+int gta04_bt_vendor_serial_open(void)
+{
+ struct termios termios;
+ int serial_fd = -1;
+ int rc;
+
+ if (gta04_bt_vendor == NULL)
+ return -1;
+
+ serial_fd = open(serial_path, O_RDWR | O_NONBLOCK);
+ if (serial_fd < 0) {
+ ALOGE("%s: Opening serial failed", __func__);
+ goto error;
+ }
+
+ tcflush(serial_fd, TCIOFLUSH);
+
+ rc = tcgetattr(serial_fd, &termios);
+ if (rc < 0) {
+ ALOGE("%s: Getting serial attributes failed", __func__);
+ goto error;
+ }
+
+ cfmakeraw(&termios);
+
+ cfsetispeed(&termios, serial_init_speed);
+ cfsetospeed(&termios, serial_init_speed);
+
+ rc = tcsetattr(serial_fd, TCSANOW, &termios);
+ if (rc < 0) {
+ ALOGE("%s: Setting serial attributes failed", __func__);
+ goto error;
+ }
+
+ tcflush(serial_fd, TCIOFLUSH);
+
+ gta04_bt_vendor->serial_fd = serial_fd;
+
+ rc = 1;
+ goto complete;
+
+error:
+ if (serial_fd >= 0)
+ close(serial_fd);
+
+ gta04_bt_vendor->serial_fd = -1;
+
+ rc = -1;
+
+complete:
+ return rc;
+}
+
+int gta04_bt_vendor_serial_close(void)
+{
+ if (gta04_bt_vendor == NULL)
+ return -1;
+
+ if (gta04_bt_vendor->serial_fd < 0)
+ return 0;
+
+ close(gta04_bt_vendor->serial_fd);
+ gta04_bt_vendor->serial_fd = -1;
+
+ return 0;
+}
+
+int gta04_bt_vendor_serial_read(void *buffer, size_t length)
+{
+ if (buffer == NULL || length == 0)
+ return -EINVAL;
+
+ if (gta04_bt_vendor == NULL || gta04_bt_vendor->serial_fd < 0)
+ return -1;
+
+ return read(gta04_bt_vendor->serial_fd, buffer, length);
+}
+
+int gta04_bt_vendor_serial_write(void *buffer, size_t length)
+{
+ unsigned int count;
+ unsigned char *p;
+ int rc;
+
+ if (buffer == NULL || length == 0)
+ return -EINVAL;
+
+ if (gta04_bt_vendor == NULL || gta04_bt_vendor->serial_fd < 0)
+ return -1;
+
+ p = (unsigned char *) buffer;
+ count = 0;
+
+ while (count < length) {
+ rc = write(gta04_bt_vendor->serial_fd, p, length - count);
+ if (rc <= 0)
+ return -1;
+
+ p += rc;
+ count += rc;
+ }
+
+ return count;
+}
+
+int gta04_bt_vendor_serial_work_speed(void)
+{
+ struct termios termios;
+ int rc;
+
+ if (gta04_bt_vendor == NULL || gta04_bt_vendor->serial_fd < 0)
+ return -1;
+
+ tcflush(gta04_bt_vendor->serial_fd, TCIOFLUSH);
+
+ rc = tcgetattr(gta04_bt_vendor->serial_fd, &termios);
+ if (rc < 0) {
+ ALOGE("%s: Getting serial attributes failed", __func__);
+ return -1;
+ }
+
+ cfsetispeed(&termios, serial_work_speed);
+ cfsetospeed(&termios, serial_work_speed);
+
+ rc = tcsetattr(gta04_bt_vendor->serial_fd, TCSANOW, &termios);
+ if (rc < 0) {
+ ALOGE("%s: Setting serial attributes failed", __func__);
+ return -1;
+ }
+
+ tcflush(gta04_bt_vendor->serial_fd, TCIOFLUSH);
+
+ return 0;
+}
+
+/*
+ * Bluetooth Interface
+ */
+
+int gta04_bt_vendor_init(const bt_vendor_callbacks_t *callbacks,
+ unsigned char *local_bdaddr)
+{
+ int rc;
+
+ ALOGD("%s(%p, %p)", __func__, callbacks, local_bdaddr);
+
+ if (callbacks == NULL || local_bdaddr == NULL)
+ return -EINVAL;
+
+ if (gta04_bt_vendor != NULL)
+ free(gta04_bt_vendor);
+
+ gta04_bt_vendor = (struct gta04_bt_vendor *) calloc(1, sizeof(struct gta04_bt_vendor));
+ gta04_bt_vendor->callbacks = (bt_vendor_callbacks_t *) callbacks;
+
+ rc = 0;
+ goto complete;
+
+error:
+ if (gta04_bt_vendor != NULL) {
+ free(gta04_bt_vendor);
+ gta04_bt_vendor = NULL;
+ }
+
+ rc = -1;
+
+complete:
+ return rc;
+}
+
+void gta04_bt_vendor_cleanup(void)
+{
+ if (gta04_bt_vendor == NULL)
+ return;
+
+ free(gta04_bt_vendor);
+ gta04_bt_vendor = NULL;
+}
+
+void gta04_bt_vendor_op_fw_cfg_callback(void *response)
+{
+ void *buffer, *data;
+ size_t length, size;
+ int rc;
+
+ ALOGD("%s(%p)", __func__, response);
+
+ if (response == NULL)
+ return;
+
+ if (gta04_bt_vendor == NULL || gta04_bt_vendor->callbacks == NULL || gta04_bt_vendor->callbacks->fwcfg_cb == NULL)
+ return;
+
+ // Perform warm reset so that the work UART speed can take effect
+
+ length = BT_HC_HDR_SIZE + HCI_CMD_PREAMBLE_SIZE + 1 + HCI_BCCMD_WORDS_COUNT * sizeof(unsigned short);
+ buffer = calloc(1, length);
+
+ gta04_bt_vendor_hci_cmd_prepare(buffer, length);
+
+ data = (void *) ((unsigned char *) buffer + BT_HC_HDR_SIZE);
+ size = length - BT_HC_HDR_SIZE;
+
+ gta04_bt_vendor_hci_preamble(data, size, HCI_CMD_VENDOR);
+
+ data = (void *) ((unsigned char *) buffer + BT_HC_HDR_SIZE + HCI_CMD_PREAMBLE_SIZE);
+ size = length - BT_HC_HDR_SIZE - HCI_CMD_PREAMBLE_SIZE;
+
+ gta04_bt_vendor_hci_bccmd(data, size, HCI_BCCMD_PDU_SETREQ, HCI_BCCMD_VARID_WARM_RESET);
+
+ rc = gta04_bt_vendor_hci_h4_write(buffer, length);
+ if (rc < 0) {
+ ALOGE("%s: Writing HCI failed", __func__);
+ return;
+ }
+
+ free(buffer);
+
+ sleep(1);
+
+ rc = gta04_bt_vendor_serial_work_speed();
+ if (rc < 0) {
+ ALOGD("%s: Switching to serial work speed failed", __func__);
+ return;
+ }
+
+ ALOGD("%s: Switched to serial work speed", __func__);
+
+ gta04_bt_vendor->callbacks->fwcfg_cb(BT_VND_OP_RESULT_SUCCESS);
+}
+
+int gta04_bt_vendor_op_fw_cfg(void *param)
+{
+ void *buffer, *data;
+ size_t length, size;
+ unsigned short speed;
+ unsigned char *p;
+
+ ALOGD("%s(%p)", __func__, param);
+
+ if (gta04_bt_vendor == NULL || gta04_bt_vendor->callbacks == NULL || gta04_bt_vendor->callbacks->fwcfg_cb == NULL || gta04_bt_vendor->callbacks->alloc == NULL || gta04_bt_vendor->callbacks->xmit_cb == NULL)
+ return -1;
+
+ if (serial_init_speed == serial_work_speed) {
+ gta04_bt_vendor->callbacks->fwcfg_cb(BT_VND_OP_RESULT_SUCCESS);
+ return 0;
+ }
+
+ // Set work UART speed to PSRAM
+
+ speed = gta04_bt_vendor_hci_bccmd_speed(serial_work_speed);
+
+ length = BT_HC_HDR_SIZE + HCI_CMD_PREAMBLE_SIZE + 1 + HCI_BCCMD_WORDS_COUNT * sizeof(unsigned short);
+ buffer = gta04_bt_vendor->callbacks->alloc(length);
+
+ memset(buffer, 0, length);
+
+ gta04_bt_vendor_hci_cmd_prepare(buffer, length);
+
+ data = (void *) ((unsigned char *) buffer + BT_HC_HDR_SIZE);
+ size = length - BT_HC_HDR_SIZE;
+
+ gta04_bt_vendor_hci_preamble(data, size, HCI_CMD_VENDOR);
+
+ data = (void *) ((unsigned char *) buffer + BT_HC_HDR_SIZE + HCI_CMD_PREAMBLE_SIZE);
+ size = length - BT_HC_HDR_SIZE - HCI_CMD_PREAMBLE_SIZE;
+
+ gta04_bt_vendor_hci_bccmd(data, size, HCI_BCCMD_PDU_SETREQ, HCI_BCCMD_VARID_PS);
+
+ p = (unsigned char *) data + 1 + 5 * sizeof(unsigned short);
+
+ p[0] = HCI_BCCMD_PSKEY_UART_BAUDRATE & 0xff;
+ p[1] = HCI_BCCMD_PSKEY_UART_BAUDRATE >> 8;
+ p[2] = 0x01;
+ p[3] = 0x00;
+ p[4] = HCI_BCCMD_STORES_PSRAM & 0xff;
+ p[5] = HCI_BCCMD_STORES_PSRAM >> 8;
+ p[6] = speed & 0xff;
+ p[7] = speed >> 8;
+
+ gta04_bt_vendor->callbacks->xmit_cb(HCI_CMD_VENDOR, buffer, gta04_bt_vendor_op_fw_cfg_callback);
+
+ return 0;
+}
+
+int gta04_bt_vendor_op_userial_open(void *param)
+{
+ void *buffer, *data;
+ size_t length, size;
+ unsigned int i;
+ int *fds;
+ int rc;
+
+ ALOGD("%s(%p)", __func__, param);
+
+ if (param == NULL)
+ return -EINVAL;
+
+ if (gta04_bt_vendor == NULL)
+ return -1;
+
+ fds = (int *) param;
+
+ rc = gta04_bt_vendor_serial_open();
+ if (rc < 0 || gta04_bt_vendor->serial_fd < 0) {
+ ALOGD("%s: Opening serial failed", __func__);
+ return -1;
+ }
+
+ length = BT_HC_HDR_SIZE + HCI_CMD_PREAMBLE_SIZE;
+ buffer = calloc(1, length);
+
+ gta04_bt_vendor_hci_cmd_prepare(buffer, length);
+
+ data = (void *) ((unsigned char *) buffer + BT_HC_HDR_SIZE);
+ size = length - BT_HC_HDR_SIZE;
+
+ gta04_bt_vendor_hci_preamble(data, size, HCI_CMD_RESET);
+
+ rc = gta04_bt_vendor_hci_h4_write(buffer, length);
+ if (rc < 0) {
+ ALOGE("%s: Writing HCI failed", __func__);
+ return -1;
+ }
+
+ free(buffer);
+
+ sleep(1);
+
+ rc = gta04_bt_vendor_hci_h4_read_event(NULL, 0, HCI_EVENT_VENDOR);
+ if (rc < 0) {
+ ALOGE("%s: Reading HCI failed", __func__);
+ return -1;
+ }
+
+ for (i = 0; i < CH_MAX; i++)
+ fds[i] = gta04_bt_vendor->serial_fd;
+
+ return 1;
+}
+
+int gta04_bt_vendor_op_userial_close(void *param)
+{
+ ALOGD("%s(%p)", __func__, param);
+
+ return gta04_bt_vendor_serial_close();
+}
+
+int gta04_bt_vendor_op(bt_vendor_opcode_t opcode, void *param)
+{
+ ALOGD("%s(%d, %p)", __func__, opcode, param);
+
+ switch (opcode) {
+ case BT_VND_OP_POWER_CTRL:
+ return 0;
+ case BT_VND_OP_FW_CFG:
+ return gta04_bt_vendor_op_fw_cfg(param);
+ case BT_VND_OP_SCO_CFG:
+ gta04_bt_vendor->callbacks->scocfg_cb(BT_VND_OP_RESULT_SUCCESS);
+ return 0;
+ case BT_VND_OP_USERIAL_OPEN:
+ return gta04_bt_vendor_op_userial_open(param);
+ case BT_VND_OP_USERIAL_CLOSE:
+ return gta04_bt_vendor_op_userial_close(param);
+ case BT_VND_OP_GET_LPM_IDLE_TIMEOUT:
+ *((int *) param) = 0;
+ return 0;
+ case BT_VND_OP_LPM_SET_MODE:
+ gta04_bt_vendor->callbacks->lpm_cb(BT_VND_OP_RESULT_SUCCESS);
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Interface
+ */
+
+const bt_vendor_interface_t BLUETOOTH_VENDOR_LIB_INTERFACE = {
+ .size = sizeof(bt_vendor_interface_t),
+ .init = gta04_bt_vendor_init,
+ .cleanup = gta04_bt_vendor_cleanup,
+ .op = gta04_bt_vendor_op,
+};
diff --git a/bt-vendor/gta04_bt_vendor.h b/bt-vendor/gta04_bt_vendor.h
new file mode 100644
index 0000000..dad5a5f
--- /dev/null
+++ b/bt-vendor/gta04_bt_vendor.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2014 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <termios.h>
+
+#include <bt_vendor_lib.h>
+
+#ifndef _GTA04_BT_VENDOR_H_
+#define _GTA04_BT_VENDOR_H_
+
+/*
+ * Values
+ */
+
+#define HCI_H4_TYPE_CMD 1
+#define HCI_H4_TYPE_ACL_DATA 2
+#define HCI_H4_TYPE_SCO_DATA 3
+#define HCI_H4_TYPE_EVENT 4
+
+#define HCI_CMD_VENDOR 0xFC00
+#define HCI_CMD_RESET 0x0C03
+#define HCI_CMD_LOCAL_VERSION_INFO 0x1001
+
+#define HCI_CMD_PREAMBLE_SIZE 3
+
+#define HCI_EVENT_VENDOR 0xFF
+
+#define HCI_BCCMD_PDU_GETREQ 0x0000
+#define HCI_BCCMD_PDU_GETRESP 0x0001
+#define HCI_BCCMD_PDU_SETREQ 0x0002
+#define HCI_BCCMD_STAT_OK 0x0000
+
+#define HCI_BCCMD_DESCRIPTOR 0xC2
+#define HCI_BCCMD_WORDS_COUNT 9
+
+#define HCI_BCCMD_STORES_PSI 0x0001
+#define HCI_BCCMD_STORES_PSF 0x0002
+#define HCI_BCCMD_STORES_PSROM 0x0004
+#define HCI_BCCMD_STORES_PSRAM 0x0008
+
+#define HCI_BCCMD_VARID_WARM_RESET 0x4002
+#define HCI_BCCMD_VARID_PS 0x7003
+#define HCI_BCCMD_PSKEY_UART_BAUDRATE 0x01BE
+
+/*
+ * Structures
+ */
+
+struct gta04_bt_vendor {
+ bt_vendor_callbacks_t *callbacks;
+
+ int serial_fd;
+};
+
+/*
+ * Globals
+ */
+
+extern const char serial_path[];
+extern const speed_t serial_init_speed;
+extern const speed_t serial_work_speed;
+
+extern struct gta04_bt_vendor *gta04_bt_vendor;
+
+/*
+ * Declarations
+ */
+
+// GTA04 BT Vendor
+
+int gta04_bt_vendor_serial_open(void);
+int gta04_bt_vendor_serial_close(void);
+int gta04_bt_vendor_serial_read(void *buffer, size_t length);
+int gta04_bt_vendor_serial_write(void *buffer, size_t length);
+int gta04_bt_vendor_serial_work_speed(void);
+
+// HCI
+
+int gta04_bt_vendor_hci_cmd_prepare(void *buffer, size_t length);
+int gta04_bt_vendor_hci_preamble(void *buffer, size_t length,
+ unsigned short cmd);
+int gta04_bt_vendor_hci_bccmd(void *buffer, size_t length,
+ unsigned short pdu, unsigned short varid);
+unsigned short gta04_bt_vendor_hci_bccmd_speed(speed_t speed);
+int gta04_bt_vendor_hci_h4_write(void *buffer, size_t length);
+int gta04_bt_vendor_hci_h4_read_event(void *buffer, size_t length,
+ unsigned char event);
+
+#endif
diff --git a/bt-vendor/hci.c b/bt-vendor/hci.c
new file mode 100644
index 0000000..c83d3e1
--- /dev/null
+++ b/bt-vendor/hci.c
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2014 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <termios.h>
+
+#define LOG_TAG "gta04_bt_vendor"
+#include <cutils/log.h>
+
+#include <bt_vendor_lib.h>
+#include <bt_hci_bdroid.h>
+
+#include "gta04_bt_vendor.h"
+
+int gta04_bt_vendor_hci_cmd_prepare(void *buffer, size_t length)
+{
+ HC_BT_HDR *header;
+
+ if (buffer == NULL || length < BT_HC_HDR_SIZE)
+ return -EINVAL;
+
+ header = (HC_BT_HDR *) buffer;
+ header->event = MSG_STACK_TO_HC_HCI_CMD;
+ header->len = length - BT_HC_HDR_SIZE;
+ header->offset = 0;
+ header->layer_specific = 0;
+
+ return 0;
+}
+
+int gta04_bt_vendor_hci_preamble(void *buffer, size_t length,
+ unsigned short cmd)
+{
+ unsigned char *p;
+
+ if (buffer == NULL || length < HCI_CMD_PREAMBLE_SIZE)
+ return -EINVAL;
+
+ p = (unsigned char *) buffer;
+
+ p[0] = cmd & 0xff;
+ p[1] = cmd >> 8;
+ p[2] = length - HCI_CMD_PREAMBLE_SIZE;
+
+ return 0;
+}
+
+int gta04_bt_vendor_hci_bccmd(void *buffer, size_t length,
+ unsigned short pdu, unsigned short varid)
+{
+ static unsigned bccmd_seq = 0;
+ size_t bccmd_words_count = HCI_BCCMD_WORDS_COUNT;
+ size_t bccmd_length = bccmd_words_count * sizeof(unsigned short) + 1;
+ unsigned char *p;
+
+ if (buffer == NULL || length < bccmd_length)
+ return -EINVAL;
+
+ memset(buffer, 0, 11);
+
+ bccmd_words_count = (length - 1) / 2;
+
+ p = (unsigned char *) buffer;
+
+ // Descriptor
+ p[0] = HCI_BCCMD_DESCRIPTOR;
+
+ // Payload
+ p[1] = pdu & 0xff;
+ p[2] = pdu >> 8;
+ p[3] = bccmd_words_count & 0xff;
+ p[4] = bccmd_words_count >> 8;
+ p[5] = bccmd_seq & 0xff;
+ p[6] = bccmd_seq >> 8;
+ p[7] = varid & 0xff;
+ p[8] = varid >> 8;
+ p[9] = HCI_BCCMD_STAT_OK & 0xff;
+ p[10] = HCI_BCCMD_STAT_OK >> 8;
+
+ bccmd_seq++;
+
+ return 0;
+}
+
+unsigned short gta04_bt_vendor_hci_bccmd_speed(speed_t speed)
+{
+ switch (speed) {
+ case B0:
+ case B50:
+ case B75:
+ case B110:
+ case B134:
+ case B150:
+ case B200:
+ return 0;
+ case B300:
+ return 1;
+ case B600:
+ return 3;
+ case B1200:
+ return 5;
+ case B1800:
+ return 7;
+ case B2400:
+ return 10;
+ case B4800:
+ return 20;
+ case B9600:
+ return 39;
+ case B19200:
+ return 79;
+ case B38400:
+ return 157;
+ case B57600:
+ return 236;
+ case B115200:
+ return 472;
+ case B230400:
+ return 944;
+ case B460800:
+ return 1887;
+ case B500000:
+ return 2048;
+ case B576000:
+ return 2359;
+ case B921600:
+ return 3775;
+ case B1000000:
+ return 4096;
+ case B1152000:
+ return 4719;
+ case B1500000:
+ return 6144;
+ case B2000000:
+ return 8192;
+ case B2500000:
+ return 10240;
+ case B3000000:
+ return 12288;
+ case B3500000:
+ return 14336;
+ case B4000000:
+ return 16384;
+ default:
+ return 12288;
+ }
+}
+
+int gta04_bt_vendor_hci_h4_write(void *buffer, size_t length)
+{
+ void *data;
+ size_t size;
+ unsigned char *p;
+ int rc;
+
+ if (buffer == NULL || length < BT_HC_HDR_SIZE + HCI_CMD_PREAMBLE_SIZE)
+ return -EINVAL;
+
+ p = (unsigned char *) buffer + BT_HC_HDR_SIZE - 1;
+
+ data = (void *) p;
+ size = length - BT_HC_HDR_SIZE + 1;
+
+ p[0] = HCI_H4_TYPE_CMD;
+
+ rc = gta04_bt_vendor_serial_write(data, size);
+ if (rc < 0) {
+ ALOGE("%s: Writing to serial failed", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int gta04_bt_vendor_hci_h4_read_event(void *buffer, size_t length,
+ unsigned char event)
+{
+ struct timeval timeout;
+ unsigned char cleanup[256];
+ unsigned char byte;
+ unsigned char type = 0;
+ unsigned char size = 0;
+ unsigned char *p = NULL;
+ unsigned int count;
+ unsigned int limit;
+ int failures;
+ int skip = 0;
+ fd_set fds;
+ int rc;
+
+ if (gta04_bt_vendor == NULL || gta04_bt_vendor->serial_fd < 0)
+ return -1;
+
+ failures = 0;
+
+ while (1) {
+ FD_ZERO(&fds);
+ FD_SET(gta04_bt_vendor->serial_fd, &fds);
+
+ timeout.tv_sec = 5;
+ timeout.tv_usec = 0;
+
+ rc = select(gta04_bt_vendor->serial_fd + 1, &fds, NULL, NULL, &timeout);
+ if (rc < 0) {
+ ALOGE("%s: Polling failed", __func__);
+
+ if (failures) {
+ return -1;
+ } else {
+ failures++;
+ continue;
+ }
+ } else if (rc == 0) {
+ ALOGE("%s: Polling timed out", __func__);
+ return -1;
+ }
+
+ if (failures > 10) {
+ ALOGE("%s: Too many failures", __func__);
+ return -1;
+ }
+
+ if (p == NULL) {
+ // Read single bytes first
+
+ rc = gta04_bt_vendor_serial_read(&byte, sizeof(byte));
+ if (rc < (int) sizeof(byte)) {
+ ALOGE("%s: Reading from serial failed", __func__);
+ return -1;
+ }
+
+ if (type != HCI_H4_TYPE_EVENT) {
+ if (byte != HCI_H4_TYPE_EVENT) {
+ ALOGD("%s: Ignored response with type: %d", __func__, byte);
+ gta04_bt_vendor_serial_read(&cleanup, sizeof(cleanup));
+ continue;
+ }
+
+ type = byte;
+ } else {
+ if (byte != event) {
+ ALOGD("%s: Ignored response with event: 0x%x", __func__, byte);
+ gta04_bt_vendor_serial_read(&cleanup, sizeof(cleanup));
+ type = 0;
+ continue;
+ }
+
+ rc = gta04_bt_vendor_serial_read(&size, sizeof(size));
+ if (rc <= 0) {
+ ALOGE("%s: Reading from serial failed", __func__);
+ failures++;
+ continue;
+ } else if (failures) {
+ failures = 0;
+ }
+
+
+ if (size == 0)
+ return 0;
+
+ if (buffer == NULL || length == 0) {
+ count = 0;
+
+ while (count < size) {
+ rc = gta04_bt_vendor_serial_read(&cleanup, size - count);
+ if (rc <= 0) {
+ ALOGE("%s: Reading from serial failed", __func__);
+ failures++;
+ break;
+ } else if (failures) {
+ failures = 0;
+ }
+
+ count += rc;
+ }
+
+ if (rc <= 0)
+ continue;
+
+ return 0;
+ }
+
+ p = (unsigned char *) buffer;
+ }
+ }
+
+ if (p != NULL) {
+ // Make sure to read from the start in case of failure
+ p = (unsigned char *) buffer;
+
+ if (size > length) {
+ ALOGE("%s: Provided buffer length is too small for size: %d/%d bytes", __func__, length, size);
+
+ limit = length;
+ } else {
+ limit = size;
+ }
+
+ count = 0;
+
+ while (count < limit) {
+ rc = gta04_bt_vendor_serial_read(p, limit - count);
+ if (rc <= 0) {
+ ALOGE("%s: Reading from serial failed", __func__);
+ failures++;
+ break;
+ } else if (failures) {
+ failures = 0;
+ }
+
+ p += rc;
+ count += rc;
+ }
+
+ if (rc <= 0)
+ continue;
+
+ if (limit < size) {
+ limit = size;
+
+ while (count < limit) {
+ rc = gta04_bt_vendor_serial_read(&cleanup, limit - count);
+ if (rc <= 0) {
+ ALOGE("%s: Reading from serial failed", __func__);
+ failures++;
+ break;
+ } else if (failures) {
+ failures = 0;
+ }
+
+ count += rc;
+ }
+ }
+
+ if (rc <= 0)
+ continue;
+
+ return 0;
+ }
+ }
+
+ return 0;
+}