diff options
Diffstat (limited to 'bt-vendor')
-rw-r--r-- | bt-vendor/Android.mk | 30 | ||||
-rw-r--r-- | bt-vendor/gta04_bt_vendor.c | 429 | ||||
-rw-r--r-- | bt-vendor/gta04_bt_vendor.h | 104 | ||||
-rw-r--r-- | bt-vendor/hci.c | 359 |
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; +} |