diff options
author | Zach Johnson <zachoverflow@google.com> | 2014-08-15 17:00:17 -0700 |
---|---|---|
committer | Andre Eisenbach <eisenbach@google.com> | 2015-03-16 16:51:29 -0700 |
commit | fbbd42b1fc1aae7a106f46275ab1fd86452cce78 (patch) | |
tree | f007ac5ff92281416aea266b30a88262182781e0 | |
parent | d18cfd09c96740f57c5645b18d9582aa8afffced (diff) | |
download | android_system_bt-fbbd42b1fc1aae7a106f46275ab1fd86452cce78.tar.gz android_system_bt-fbbd42b1fc1aae7a106f46275ab1fd86452cce78.tar.bz2 android_system_bt-fbbd42b1fc1aae7a106f46275ab1fd86452cce78.zip |
Refactor HCI layer
Refactor the HCI layer for better testability.
A future CL will add a HAL implementation for multi channel transport.
Since btsnoop and vendor are modified, it's not easy to incrementally introduce these changes.
-rw-r--r-- | hci/Android.mk | 66 | ||||
-rw-r--r-- | hci/include/btsnoop.h | 36 | ||||
-rw-r--r-- | hci/include/hci.h | 86 | ||||
-rw-r--r-- | hci/include/hci_hal.h | 84 | ||||
-rw-r--r-- | hci/include/hci_inject.h | 19 | ||||
-rw-r--r-- | hci/include/hci_internals.h | 31 | ||||
-rw-r--r-- | hci/include/hci_layer.h | 120 | ||||
-rw-r--r-- | hci/include/low_power_manager.h | 47 | ||||
-rw-r--r-- | hci/include/packet_fragmenter.h | 57 | ||||
-rw-r--r-- | hci/include/utils.h | 194 | ||||
-rw-r--r-- | hci/include/vendor.h | 51 | ||||
-rw-r--r-- | hci/src/bt_hci_bdroid.c | 506 | ||||
-rw-r--r-- | hci/src/btsnoop.c | 42 | ||||
-rw-r--r-- | hci/src/hci_h4.c | 1062 | ||||
-rw-r--r-- | hci/src/hci_hal_h4.c | 224 | ||||
-rw-r--r-- | hci/src/hci_inject.c | 29 | ||||
-rw-r--r-- | hci/src/hci_layer.c | 677 | ||||
-rw-r--r-- | hci/src/low_power_manager.c | 242 | ||||
-rw-r--r-- | hci/src/lpm.c | 362 | ||||
-rw-r--r-- | hci/src/packet_fragmenter.c | 232 | ||||
-rw-r--r-- | hci/src/userial.c | 418 | ||||
-rw-r--r-- | hci/src/utils.c | 309 | ||||
-rw-r--r-- | hci/src/vendor.c | 148 | ||||
-rw-r--r-- | hci/test/hci_hal_h4_test.cpp | 259 | ||||
-rw-r--r-- | hci/test/hci_layer_test.cpp | 584 | ||||
-rw-r--r-- | hci/test/low_power_manager_test.cpp | 140 | ||||
-rw-r--r-- | hci/test/packet_fragmenter_test.cpp | 367 | ||||
-rwxr-xr-x | main/bte_main.c | 275 |
28 files changed, 3413 insertions, 3254 deletions
diff --git a/hci/Android.mk b/hci/Android.mk index 8a5c3adf5..56f89f40e 100644 --- a/hci/Android.mk +++ b/hci/Android.mk @@ -5,13 +5,13 @@ include $(CLEAR_VARS) LOCAL_CFLAGS += $(bdroid_CFLAGS) LOCAL_SRC_FILES := \ - src/bt_hci_bdroid.c \ - src/btsnoop.c \ - src/btsnoop_net.c \ - src/hci_inject.c \ - src/lpm.c \ - src/utils.c \ - src/vendor.c + src/btsnoop.c \ + src/btsnoop_net.c \ + src/hci_inject.c \ + src/hci_layer.c \ + src/low_power_manager.c \ + src/packet_fragmenter.c \ + src/vendor.c LOCAL_CFLAGS := -Wno-unused-parameter @@ -20,27 +20,59 @@ ifeq ($(BLUETOOTH_HCI_USE_MCT),true) LOCAL_CFLAGS += -DHCI_USE_MCT LOCAL_SRC_FILES += \ - src/hci_mct.c \ - src/userial_mct.c + src/hci_mct.c \ + src/userial_mct.c else LOCAL_SRC_FILES += \ - src/hci_h4.c \ - src/userial.c + src/hci_hal_h4.c + endif LOCAL_CFLAGS += -std=c99 LOCAL_C_INCLUDES += \ - $(LOCAL_PATH)/include \ - $(LOCAL_PATH)/../osi/include \ - $(LOCAL_PATH)/../utils/include \ - $(LOCAL_PATH)/../stack/include \ - $(LOCAL_PATH)/../gki/ulinux \ - $(bdroid_C_INCLUDES) + $(LOCAL_PATH)/include \ + $(LOCAL_PATH)/../include \ + $(LOCAL_PATH)/../gki/common \ + $(LOCAL_PATH)/../gki/ulinux \ + $(LOCAL_PATH)/../osi/include \ + $(LOCAL_PATH)/../stack/include \ + $(LOCAL_PATH)/../utils/include \ + $(bdroid_C_INCLUDES) LOCAL_MODULE := libbt-hci LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := STATIC_LIBRARIES include $(BUILD_STATIC_LIBRARY) + +##################################################### + +include $(CLEAR_VARS) + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/include \ + $(LOCAL_PATH)/../include \ + $(LOCAL_PATH)/../gki/common \ + $(LOCAL_PATH)/../gki/ulinux \ + $(LOCAL_PATH)/../osi/include \ + $(LOCAL_PATH)/../osi/test \ + $(LOCAL_PATH)/../stack/include \ + $(LOCAL_PATH)/../utils/include \ + $(bdroid_C_INCLUDES) + +LOCAL_SRC_FILES := \ + ../osi/test/AlarmTestHarness.cpp \ + ./test/hci_hal_h4_test.cpp \ + ./test/hci_layer_test.cpp \ + ./test/low_power_manager_test.cpp \ + ./test/packet_fragmenter_test.cpp + +LOCAL_CFLAGS := -Wall -Werror +LOCAL_MODULE := libbt-hcitests +LOCAL_MODULE_TAGS := tests +LOCAL_SHARED_LIBRARIES := liblog libdl +LOCAL_STATIC_LIBRARIES := libbt-hci libosi + +include $(BUILD_NATIVE_TEST) diff --git a/hci/include/btsnoop.h b/hci/include/btsnoop.h index 83c810711..614bbf017 100644 --- a/hci/include/btsnoop.h +++ b/hci/include/btsnoop.h @@ -1,10 +1,38 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * 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. + * + ******************************************************************************/ + #pragma once #include <stdbool.h> -#include "bt_hci_bdroid.h" +#include "bt_types.h" + +typedef struct btsnoop_interface_t { + // Open btsnoop, and dump captured packets to the file pointed to by |path|. + void (*open)(const char *path); + + // Close btsnoop, so no more packets are captured. + void (*close)(void); -void btsnoop_open(const char *p_path, const bool save_existing); -void btsnoop_close(void); + // Capture |packet| and dump it to the btsnoop logs. If |is_received| is + // true, the packet is marked as incoming. Otherwise, the packet is marked + // as outgoing. + void (*capture)(const BT_HDR *packet, bool is_received); +} btsnoop_interface_t; -void btsnoop_capture(const HC_BT_HDR *p_buf, bool is_rcvd); +const btsnoop_interface_t *btsnoop_get_interface(); diff --git a/hci/include/hci.h b/hci/include/hci.h deleted file mode 100644 index 0bfec2b68..000000000 --- a/hci/include/hci.h +++ /dev/null @@ -1,86 +0,0 @@ -/****************************************************************************** - * - * 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.h - * - * Description: Contains definitions used for HCI transport controls - * - ******************************************************************************/ - -#ifndef HCI_H -#define HCI_H - -/****************************************************************************** -** Constants & Macros -******************************************************************************/ - -/****************************************************************************** -** Type definitions -******************************************************************************/ - -/** Prototypes for HCI Service interface functions **/ - -/* Initialize transport's control block */ -typedef void (*tHCI_INIT)(void); - -/* Do transport's control block clean-up */ -typedef void (*tHCI_CLEANUP)(void); - -/* Send HCI command/data to the transport */ -typedef void (*tHCI_SEND)(HC_BT_HDR *p_msg); - -/* Handler for HCI upstream path */ -typedef uint16_t (*tHCI_RCV)(void); - -/* Callback function for the returned event of internally issued command */ -typedef void (*tINT_CMD_CBACK)(void *p_mem); - -/* Handler for sending HCI command from the local module */ -typedef uint8_t (*tHCI_SEND_INT)(uint16_t opcode, HC_BT_HDR *p_buf, \ - tINT_CMD_CBACK p_cback); - -/* Handler for getting acl data length */ -typedef void (*tHCI_ACL_DATA_LEN_HDLR)(void); - -/****************************************************************************** -** Extern variables and functions -******************************************************************************/ - -typedef struct { - tHCI_INIT init; - tHCI_CLEANUP cleanup; - tHCI_SEND send; - tHCI_SEND_INT send_int_cmd; - tHCI_ACL_DATA_LEN_HDLR get_acl_max_len; -#ifdef HCI_USE_MCT - tHCI_RCV evt_rcv; - tHCI_RCV acl_rcv; -#else - tHCI_RCV rcv; -#endif -} tHCI_IF; - -/****************************************************************************** -** Functions -******************************************************************************/ - - -#endif /* HCI_H */ - diff --git a/hci/include/hci_hal.h b/hci/include/hci_hal.h new file mode 100644 index 000000000..3fa9a89b2 --- /dev/null +++ b/hci/include/hci_hal.h @@ -0,0 +1,84 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * 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. + * + ******************************************************************************/ + +#pragma once + +#include <stdbool.h> +#include <stdint.h> + +#include "thread.h" +#include "vendor.h" + +typedef enum { + DATA_TYPE_COMMAND = 1, + DATA_TYPE_ACL = 2, + DATA_TYPE_SCO = 3, + DATA_TYPE_EVENT = 4 +} serial_data_type_t; + +typedef void (*data_ready_cb)(serial_data_type_t type); + +typedef struct { + // Called when the HAL detects inbound data. + // Data |type| may be ACL, SCO, or EVENT. + // Executes in the context of the thread supplied to |init|. + data_ready_cb data_ready; + + /* + // Called when the HAL detects inbound astronauts named Dave. + // HAL will deny all requests to open the pod bay doors after this. + dave_ready_cb dave_ready; + */ +} hci_hal_callbacks_t; + +typedef struct hci_hal_interface_t { + // Initialize the HAL, with |upper_callbacks| and |upper_thread| to run in the context of. + bool (*init)(const hci_hal_callbacks_t *upper_callbacks, thread_t *upper_thread); + + // Connect to the underlying hardware, and let data start flowing. + bool (*open)(void); + // Disconnect from the underlying hardware, and close the HAL. + // "Daisy, Daisy..." + void (*close)(void); + + // Retrieve bytes for ACL, SCO, or EVENT data packets. + // Only guaranteed to be correct in the context of a data_ready callback + // of the corresponding type. + uint8_t (*read_byte)(serial_data_type_t type); + // Returns true if there is a byte available of the specified |type|. + bool (*has_byte)(serial_data_type_t type); + // The upper layer must call this to notify the HAL that it has finished + // reading a packet of the specified |type|. Underlying implementations that + // use shared channels for multiple data types depend on this to know when + // to reinterpret the data stream. + void (*packet_finished)(serial_data_type_t type); + // Transmit COMMAND, ACL, or SCO data packets. + // |data| may not be NULL. |length| must be greater than zero. + // + // IMPORTANT NOTE: + // Depending on the underlying implementation, the byte right + // before the beginning of |data| may be borrowed during this call + // and then restored to its original value. + // This is safe in the bluetooth context, because there is always a buffer + // header that prefixes data you're sending. + uint16_t (*transmit_data)(serial_data_type_t type, uint8_t *data, uint16_t length); +} hci_hal_interface_t; + +const hci_hal_interface_t *hci_hal_get_interface(); +const hci_hal_interface_t *hci_hal_get_test_interface(vendor_interface_t *vendor_interface); + diff --git a/hci/include/hci_inject.h b/hci/include/hci_inject.h index daef04b1a..1e9bca885 100644 --- a/hci/include/hci_inject.h +++ b/hci/include/hci_inject.h @@ -20,9 +20,18 @@ #include <stdbool.h> -// Starts the HCI injection module. Returns true on success, false on -// failure. Once started, this module must be shut down with |hci_inject_close|. -bool hci_inject_open(void); +#include "allocator.h" -// Shuts down the HCI injection module. -void hci_inject_close(void); +typedef struct hci_interface_t hci_interface_t; + +typedef struct hci_inject_interface_t { + // Starts the HCI injection module, using the provided |buffer_allocator|. + // Returns true on success, false on failure. Once started, this module + // must be shut down with |hci_inject_close|. + bool (*open)(const hci_interface_t *hci_interface, const allocator_t *buffer_allocator); + + // Shuts down the HCI injection module. + void (*close)(void); +} hci_inject_interface_t; + +const hci_inject_interface_t *hci_inject_get_interface(); diff --git a/hci/include/hci_internals.h b/hci/include/hci_internals.h new file mode 100644 index 000000000..70dacff46 --- /dev/null +++ b/hci/include/hci_internals.h @@ -0,0 +1,31 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * 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. + * + ******************************************************************************/ + +#pragma once + +// 2 bytes for opcode, 1 byte for parameter length (Volume 2, Part E, 5.4.1) +#define HCI_COMMAND_PREAMBLE_SIZE 3 +// 2 bytes for handle, 2 bytes for data length (Volume 2, Part E, 5.4.2) +#define HCI_ACL_PREAMBLE_SIZE 4 +// 2 bytes for handle, 1 byte for data length (Volume 2, Part E, 5.4.3) +#define HCI_SCO_PREAMBLE_SIZE 3 +// 1 byte for event code, 1 byte for parameter length (Volume 2, Part E, 5.4.4) +#define HCI_EVENT_PREAMBLE_SIZE 2 + +typedef void (*internal_command_cb)(void *response); + diff --git a/hci/include/hci_layer.h b/hci/include/hci_layer.h new file mode 100644 index 000000000..d6f0ac0d3 --- /dev/null +++ b/hci/include/hci_layer.h @@ -0,0 +1,120 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * 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. + * + ******************************************************************************/ + +#pragma once + +#include <stdbool.h> + +#include "allocator.h" +#include "data_dispatcher.h" +#include "osi.h" + +///// LEGACY DEFINITIONS ///// + +/* Message event mask across Host/Controller lib and stack */ +#define MSG_EVT_MASK 0xFF00 /* eq. BT_EVT_MASK */ +#define MSG_SUB_EVT_MASK 0x00FF /* eq. BT_SUB_EVT_MASK */ + +/* Message event ID passed from Host/Controller lib to stack */ +#define MSG_HC_TO_STACK_HCI_ERR 0x1300 /* eq. BT_EVT_TO_BTU_HCIT_ERR */ +#define MSG_HC_TO_STACK_HCI_ACL 0x1100 /* eq. BT_EVT_TO_BTU_HCI_ACL */ +#define MSG_HC_TO_STACK_HCI_SCO 0x1200 /* eq. BT_EVT_TO_BTU_HCI_SCO */ +#define MSG_HC_TO_STACK_HCI_EVT 0x1000 /* eq. BT_EVT_TO_BTU_HCI_EVT */ +#define MSG_HC_TO_STACK_L2C_SEG_XMIT 0x1900 /* eq. BT_EVT_TO_BTU_L2C_SEG_XMIT */ + +/* Message event ID passed from stack to vendor lib */ +#define MSG_STACK_TO_HC_HCI_ACL 0x2100 /* eq. BT_EVT_TO_LM_HCI_ACL */ +#define MSG_STACK_TO_HC_HCI_SCO 0x2200 /* eq. BT_EVT_TO_LM_HCI_SCO */ +#define MSG_STACK_TO_HC_HCI_CMD 0x2000 /* eq. BT_EVT_TO_LM_HCI_CMD */ + +/* Local Bluetooth Controller ID for BR/EDR */ +#define LOCAL_BR_EDR_CONTROLLER_ID 0 + +///// END LEGACY DEFINITIONS ///// + +typedef struct hci_hal_interface_t hci_hal_interface_t; +typedef struct btsnoop_interface_t btsnoop_interface_t; +typedef struct hci_inject_interface_t hci_inject_interface_t; +typedef struct packet_fragmenter_interface_t packet_fragmenter_interface_t; +typedef struct vendor_interface_t vendor_interface_t; +typedef struct low_power_manager_interface_t low_power_manager_interface_t; + +typedef unsigned char * bdaddr_t; + +typedef enum { + LPM_DISABLE, + LPM_ENABLE, + LPM_WAKE_ASSERT, + LPM_WAKE_DEASSERT +} low_power_command_t; + +typedef void (*preload_finished_cb)(bool success); +typedef void (*transmit_finished_cb)(void *buffer, bool all_fragments_sent); + +typedef struct { + // Called when the HCI layer finishes the preload sequence. + preload_finished_cb preload_finished; + + // Called when the HCI layer finishes sending a packet. + transmit_finished_cb transmit_finished; +} hci_callbacks_t; + +typedef struct hci_interface_t { + // Initialize the hci layer, with the specified |local_bdaddr|. + bool (*init)( + bdaddr_t local_bdaddr, + const allocator_t *upward_buffer_allocator, + const hci_callbacks_t *upper_callbacks + ); + + // Tear down and relese all resources + void (*cleanup)(void); + + // Turn the Bluetooth chip on or off, depending on |value|. + void (*set_chip_power_on)(bool value); + + // Send a low power command, if supported and the low power manager is enabled. + void (*send_low_power_command)(low_power_command_t command); + + // Do the preload sequence (call before the rest of the BT stack initializes). + void (*do_preload)(void); + + // Do the postload sequence (call after the rest of the BT stack initializes). + void (*do_postload)(void); + + // Turn logging on, and log to the specified |path|. + void (*turn_on_logging)(const char *path); + + void (*turn_off_logging)(void); + + // Register with this data dispatcher to receive data flowing upward out of the HCI layer + data_dispatcher_t *upward_dispatcher; + + // Send some data downward through the HCI layer + void (*transmit_downward)(data_dispatcher_type_t type, void *data); +} hci_interface_t; + +const hci_interface_t *hci_layer_get_interface(); + +const hci_interface_t *hci_layer_get_test_interface( + const hci_hal_interface_t *hal_interface, + const btsnoop_interface_t *btsnoop_interface, + const hci_inject_interface_t *hci_inject_interface, + const packet_fragmenter_interface_t *packet_fragmenter_interface, + const vendor_interface_t *vendor_interface, + const low_power_manager_interface_t *low_power_manager_interface); diff --git a/hci/include/low_power_manager.h b/hci/include/low_power_manager.h new file mode 100644 index 000000000..e5da615f1 --- /dev/null +++ b/hci/include/low_power_manager.h @@ -0,0 +1,47 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * 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. + * + ******************************************************************************/ + +#pragma once + +#include "hci_layer.h" + +typedef struct thread_t thread_t; +typedef struct vendor_interface_t vendor_interface_t; + +typedef struct low_power_manager_interface_t { + // Initialize the low power manager, and use |post_thread| to synchronize actions. + void (*init)(thread_t *post_thread); + + // Clean up the low power manager and release resources. + void (*cleanup)(void); + + // Performs |command| synchronized on the thread that was provided + // at initialization time. + void (*post_command)(low_power_command_t command); + + // Assert wake (for transmission). Must be called on the thread provided + // at initialization time. + void (*wake_assert)(void); + + // Tell the low power manager that you're done transmitting data. Must be + // called on the thread provided at initialization time. + void (*transmit_done)(void); +} low_power_manager_interface_t; + +const low_power_manager_interface_t *low_power_manager_get_interface(); +const low_power_manager_interface_t *low_power_manager_get_test_interface(const vendor_interface_t *vendor_interface); diff --git a/hci/include/packet_fragmenter.h b/hci/include/packet_fragmenter.h new file mode 100644 index 000000000..7d13c905b --- /dev/null +++ b/hci/include/packet_fragmenter.h @@ -0,0 +1,57 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * 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. + * + ******************************************************************************/ + +#pragma once + +#include "bt_types.h" +#include "hci_layer.h" + +typedef void (*packet_reassembled_cb)(BT_HDR *packet); +typedef void (*packet_fragmented_cb)(BT_HDR *packet, bool send_transmit_finished); + +typedef struct { + // Called for every packet fragment. + packet_fragmented_cb fragmented; + + // Called for every completely reassembled packet. + packet_reassembled_cb reassembled; + + // Called when the fragmenter finishes sending all requested fragments, + // but the packet has not been entirely sent. + transmit_finished_cb transmit_finished; +} packet_fragmenter_callbacks_t; + +typedef struct packet_fragmenter_interface_t { + // Initialize the fragmenter, specifying the |result_callbacks|. |buffer_allocator| + // is used to allocate and free buffers for the reassembled packets. + void (*init)(const packet_fragmenter_callbacks_t *result_callbacks, const allocator_t *buffer_allocator); + + // Sets the fragmentation size for normal acl data. + void (*set_acl_data_size)(uint16_t size); + // Sets the fragmentation size for bluetooth low energy acl data. + void (*set_ble_acl_data_size)(uint16_t size); + + // Fragments |packet| if necessary and hands off everything to the fragmented callback. + void (*fragment_and_dispatch)(BT_HDR *packet); + // If |packet| is a complete packet, forwards to the reassembled callback. Otherwise + // holds onto it until all fragments arrive, at which point the reassembled callback is called + // with the reassembled data. + void (*reassemble_and_dispatch)(BT_HDR *packet); +} packet_fragmenter_interface_t; + +const packet_fragmenter_interface_t *packet_fragmenter_get_interface(); diff --git a/hci/include/utils.h b/hci/include/utils.h deleted file mode 100644 index 0f1517c9f..000000000 --- a/hci/include/utils.h +++ /dev/null @@ -1,194 +0,0 @@ -/****************************************************************************** - * - * 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.h - * - * Description: Utility functions declaration - * - ******************************************************************************/ - -#ifndef UTILS_H -#define UTILS_H - -/****************************************************************************** -** Constants & Macros -******************************************************************************/ - -#define STREAM_TO_UINT16(u16, p) {u16 = ((uint16_t)(*(p)) + (((uint16_t)(*((p) + 1))) << 8)); (p) += 2;} -#define UINT16_TO_STREAM(p, u16) {*(p)++ = (uint8_t)(u16); *(p)++ = (uint8_t)((u16) >> 8);} -#define UINT32_TO_STREAM(p, u32) {*(p)++ = (uint8_t)(u32); *(p)++ = (uint8_t)((u32) >> 8); *(p)++ = (uint8_t)((u32) >> 16); *(p)++ = (uint8_t)((u32) >> 24);} - -/****************************************************************************** -** Type definitions -******************************************************************************/ - -typedef struct -{ - void *p_first; - void *p_last; - uint16_t count; -} BUFFER_Q; - -/****************************************************************************** -** Extern variables and functions -******************************************************************************/ - -/****************************************************************************** -** Functions -******************************************************************************/ - -/******************************************************************************* -** -** Function utils_init -** -** Description Utils initialization -** -** Returns None -** -*******************************************************************************/ -void utils_init (); - -/******************************************************************************* -** -** Function utils_cleanup -** -** Description Utils cleanup -** -** Returns None -** -*******************************************************************************/ -void utils_cleanup (); - -/******************************************************************************* -** -** Function utils_queue_init -** -** Description Initialize the given buffer queue -** -** Returns None -** -*******************************************************************************/ -void utils_queue_init (BUFFER_Q *p_q); - -/******************************************************************************* -** -** 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); - -/******************************************************************************* -** -** 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); - -/******************************************************************************* -** -** 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); - -/******************************************************************************* -** -** 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); - -/******************************************************************************* -** -** 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); - -/******************************************************************************* -** -** Function utils_remove_from_queue_unlocked -** -** Description Dequeue the given buffer from the middle of the given queue without lock -** -** 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); - - -/******************************************************************************* -** -** Function utils_delay -** -** Description sleep unconditionally for timeout milliseconds -** -** Returns None -** -*******************************************************************************/ -void utils_delay (uint32_t timeout); - -/******************************************************************************* -** -** Function utils_lock -** -** Description application calls this function before entering critical -** section -** -** Returns None -** -*******************************************************************************/ -void utils_lock (void); - -/******************************************************************************* -** -** Function utils_unlock -** -** Description application calls this function when leaving critical -** section -** -** Returns None -** -*******************************************************************************/ -void utils_unlock (void); - -#endif /* UTILS_H */ - diff --git a/hci/include/vendor.h b/hci/include/vendor.h index 6c13c9c25..74ea44d82 100644 --- a/hci/include/vendor.h +++ b/hci/include/vendor.h @@ -21,15 +21,50 @@ #include <stdbool.h> #include <stdint.h> +#include "allocator.h" +#include "bt_types.h" #include "bt_vendor_lib.h" +#include "hci_internals.h" -// Opens the vendor-specific library and sets the Bluetooth -// address of the adapter to |local_bdaddr|. -bool vendor_open(const uint8_t *local_bdaddr); +typedef enum { + VENDOR_CHIP_POWER_CONTROL = BT_VND_OP_POWER_CTRL, + VENDOR_OPEN_USERIAL = BT_VND_OP_USERIAL_OPEN, + VENDOR_CLOSE_USERIAL = BT_VND_OP_USERIAL_CLOSE, + VENDOR_GET_LPM_IDLE_TIMEOUT = BT_VND_OP_GET_LPM_IDLE_TIMEOUT, + VENDOR_SET_LPM_WAKE_STATE = BT_VND_OP_LPM_WAKE_SET_STATE +} vendor_opcode_t; -// Closes the vendor-specific library and frees all associated resources. -// Only |vendor_open| may be called after |vendor_close|. -void vendor_close(void); +typedef enum { + VENDOR_CONFIGURE_FIRMWARE = BT_VND_OP_FW_CFG, + VENDOR_CONFIGURE_SCO = BT_VND_OP_SCO_CFG, + VENDOR_SET_LPM_MODE = BT_VND_OP_LPM_SET_MODE, + VENDOR_DO_EPILOG = BT_VND_OP_EPILOG +} vendor_async_opcode_t; -// Sends a vendor-specific command to the library. -int vendor_send_command(bt_vendor_opcode_t opcode, void *param); +typedef void (*vendor_cb)(bool success); +typedef bool (*send_internal_command_cb)(uint16_t opcode, BT_HDR *buffer, internal_command_cb callback); + +typedef struct vendor_interface_t{ + // Opens the vendor-specific library and sets the Bluetooth + // address of the adapter to |local_bdaddr|. |allocator| is used + // when the vendor library requests buffers. + bool (*open)(const uint8_t *local_bdaddr, const allocator_t *allocator); + + // Closes the vendor-specific library and frees all associated resources. + // Only |vendor_open| may be called after |vendor_close|. + void (*close)(void); + + // Sends a vendor-specific command to the library. + int (*send_command)(vendor_opcode_t opcode, void *param); + + // Sends an asynchronous vendor-specific command to the library. + int (*send_async_command)(vendor_async_opcode_t opcode, void *param); + + // Registers a callback for an asynchronous vendor-specific command. + void (*set_callback)(vendor_async_opcode_t opcode, vendor_cb callback); + + // Set the callback for sending internal commands; + void (*set_send_internal_command_callback)(send_internal_command_cb callback); +} vendor_interface_t; + +const vendor_interface_t *vendor_get_interface(); diff --git a/hci/src/bt_hci_bdroid.c b/hci/src/bt_hci_bdroid.c deleted file mode 100644 index 0f0820ad6..000000000 --- a/hci/src/bt_hci_bdroid.c +++ /dev/null @@ -1,506 +0,0 @@ -/****************************************************************************** - * - * 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 <assert.h> -#include <utils/Log.h> - -#include "alarm.h" -#include "btsnoop.h" -#include "bt_hci_bdroid.h" -#include "bt_utils.h" -#include "bt_vendor_lib.h" -#include "hci.h" -#include "hci_inject.h" -#include "osi.h" -#include "thread.h" -#include "userial.h" -#include "utils.h" -#include "vendor.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 - -/* Vendor epilog process timeout period */ -static const uint32_t EPILOG_TIMEOUT_MS = 3000; - -/****************************************************************************** -** Externs -******************************************************************************/ - -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); - -/****************************************************************************** -** Variables -******************************************************************************/ - -bt_hc_callbacks_t *bt_hc_cbacks = NULL; -tHCI_IF *p_hci_if; -volatile bool fwcfg_acked; -// Cleanup state indication. -volatile bool has_cleaned_up = false; - -/****************************************************************************** -** Local type definitions -******************************************************************************/ - -/* Host/Controller lib thread control block */ -typedef struct -{ - thread_t *worker_thread; - pthread_mutex_t worker_thread_lock; - alarm_t *alarm; -} bt_hc_cb_t; - -/****************************************************************************** -** Static Variables -******************************************************************************/ - -static bt_hc_cb_t hc_cb; -static bool tx_cmd_pkts_pending = false; -static BUFFER_Q tx_q; - -/****************************************************************************** -** Functions -******************************************************************************/ - -static void event_preload(UNUSED_ATTR void *context) { - userial_open(USERIAL_PORT_1); - vendor_send_command(BT_VND_OP_FW_CFG, NULL); -} - -static void event_postload(UNUSED_ATTR void *context) { - /* 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 = vendor_send_command(BT_VND_OP_SCO_CFG, NULL); - if (result == -1) - p_hci_if->get_acl_max_len(); -} - -static void event_tx(UNUSED_ATTR void *context) { - /* - * 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]; - size_t sending_msg_count = 0; - int sending_hci_cmd_pkts_count = 0; - utils_lock(); - HC_BT_HDR *p_next_msg = tx_q.p_first; - while (p_next_msg && sending_msg_count < ARRAY_SIZE(sending_msg_que)) - { - 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 || - (sending_hci_cmd_pkts_count >= num_hci_cmd_pkts)) - { - tx_cmd_pkts_pending = true; - p_next_msg = utils_getnext(p_next_msg); - continue; - } - sending_hci_cmd_pkts_count++; - } - - HC_BT_HDR *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(); - for(size_t i = 0; i < sending_msg_count; i++) - p_hci_if->send(sending_msg_que[i]); - if (tx_cmd_pkts_pending) - BTHCDBG("Used up Tx Cmd credits"); -} - -static void event_rx(UNUSED_ATTR void *context) { -#ifndef HCI_USE_MCT - p_hci_if->rcv(); - - if (tx_cmd_pkts_pending && num_hci_cmd_pkts > 0) { - // Got HCI Cmd credits from controller. Send whatever data - // we have in our tx queue. We can call |event_tx| directly - // here since we're already on the worker thread. - event_tx(NULL); - } -#endif -} - -static void event_lpm_enable(UNUSED_ATTR void *context) { - lpm_enable(true); -} - -static void event_lpm_disable(UNUSED_ATTR void *context) { - lpm_enable(false); -} - -static void event_lpm_wake_device(UNUSED_ATTR void *context) { - lpm_wake_assert(); -} - -static void event_lpm_allow_sleep(UNUSED_ATTR void *context) { - lpm_allow_bt_device_sleep(); -} - -static void event_lpm_idle_timeout(UNUSED_ATTR void *context) { - lpm_wake_deassert(); -} - -static void event_epilog(UNUSED_ATTR void *context) { - vendor_send_command(BT_VND_OP_EPILOG, NULL); -} - -static void event_tx_cmd(void *msg) { - HC_BT_HDR *p_msg = (HC_BT_HDR *)msg; - - BTHCDBG("%s: p_msg: %p, event: 0x%x", __func__, p_msg, p_msg->event); - - int event = p_msg->event & MSG_EVT_MASK; - int sub_event = p_msg->event & MSG_SUB_EVT_MASK; - if (event == MSG_CTRL_TO_HC_CMD && sub_event == BT_HC_AUDIO_STATE) { - vendor_send_command(BT_VND_OP_SET_AUDIO_STATE, p_msg->data); - } else { - ALOGW("%s (event: 0x%x, sub_event: 0x%x) not supported", __func__, event, sub_event); - } - - bt_hc_cbacks->dealloc(msg); -} - -void bthc_rx_ready(void) { - pthread_mutex_lock(&hc_cb.worker_thread_lock); - - if (hc_cb.worker_thread) - thread_post(hc_cb.worker_thread, event_rx, NULL); - - pthread_mutex_unlock(&hc_cb.worker_thread_lock); -} - -void bthc_tx(HC_BT_HDR *buf) { - pthread_mutex_lock(&hc_cb.worker_thread_lock); - - if (hc_cb.worker_thread) { - if (buf) - utils_enqueue(&tx_q, buf); - thread_post(hc_cb.worker_thread, event_tx, NULL); - } - - pthread_mutex_unlock(&hc_cb.worker_thread_lock); -} - -void bthc_idle_timeout(void) { - pthread_mutex_lock(&hc_cb.worker_thread_lock); - - if (hc_cb.worker_thread) - thread_post(hc_cb.worker_thread, event_lpm_idle_timeout, NULL); - - pthread_mutex_unlock(&hc_cb.worker_thread_lock); -} - -/******************************************************************************* -** -** Function epilog_wait_timeout -** -** Description Timeout thread of epilog watchdog timer -** -** Returns None -** -*******************************************************************************/ -static void epilog_wait_timeout(UNUSED_ATTR void *context) -{ - ALOGI("...epilog_wait_timeout..."); - - thread_free(hc_cb.worker_thread); - - pthread_mutex_lock(&hc_cb.worker_thread_lock); - hc_cb.worker_thread = NULL; - pthread_mutex_unlock(&hc_cb.worker_thread_lock); -} - -/******************************************************************************* -** -** Function epilog_wait_timer -** -** Description Launch epilog watchdog timer -** -** Returns None -** -*******************************************************************************/ -static void epilog_wait_timer(void) -{ - alarm_set(hc_cb.alarm, EPILOG_TIMEOUT_MS, epilog_wait_timeout, NULL); -} - -/***************************************************************************** -** -** BLUETOOTH HOST/CONTROLLER INTERFACE LIBRARY FUNCTIONS -** -*****************************************************************************/ - -static int init(const bt_hc_callbacks_t* p_cb, unsigned char *local_bdaddr) -{ - int result; - - ALOGI("init"); - - if (p_cb == NULL) - { - ALOGE("init failed with no user callbacks!"); - return BT_HC_STATUS_FAIL; - } - - fwcfg_acked = false; - has_cleaned_up = false; - - pthread_mutex_init(&hc_cb.worker_thread_lock, NULL); - hc_cb.alarm = alarm_new(); - - /* store reference to user callbacks */ - bt_hc_cbacks = (bt_hc_callbacks_t *) p_cb; - - vendor_open(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 (hc_cb.worker_thread) - { - ALOGW("init has been called repeatedly without calling cleanup ?"); - } - - // Set prio here and let hci worker thread inherit prio - // remove once new thread api (thread_set_priority() ?) - // can switch prio - raise_priority_a2dp(TASK_HIGH_HCI_WORKER); - - hc_cb.worker_thread = thread_new_sized("bt_hc_worker", 1024); - if (!hc_cb.worker_thread) { - ALOGE("%s unable to create worker thread.", __func__); - return BT_HC_STATUS_FAIL; - } - - if (!hci_inject_open()) { - // TODO(sharvil): gracefully propagate failures from this layer. - } - - 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; - - vendor_send_command(BT_VND_OP_POWER_CTRL, &pwr_state); -} - - -/** Configure low power mode wake state */ -static int lpm(bt_hc_low_power_event_t event) -{ - switch (event) - { - case BT_HC_LPM_DISABLE: - thread_post(hc_cb.worker_thread, event_lpm_disable, NULL); - break; - - case BT_HC_LPM_ENABLE: - thread_post(hc_cb.worker_thread, event_lpm_enable, NULL); - break; - - case BT_HC_LPM_WAKE_ASSERT: - thread_post(hc_cb.worker_thread, event_lpm_wake_device, NULL); - break; - - case BT_HC_LPM_WAKE_DEASSERT: - thread_post(hc_cb.worker_thread, event_lpm_allow_sleep, NULL); - break; - } - return BT_HC_STATUS_SUCCESS; -} - - -/** Called prior to stack initialization */ -static void preload(UNUSED_ATTR TRANSAC transac) { - BTHCDBG("preload"); - thread_post(hc_cb.worker_thread, event_preload, NULL); -} - -/** Called post stack initialization */ -static void postload(UNUSED_ATTR TRANSAC transac) { - BTHCDBG("postload"); - thread_post(hc_cb.worker_thread, event_postload, NULL); -} - -/** Transmit frame */ -static int transmit_buf(TRANSAC transac, UNUSED_ATTR char *p_buf, UNUSED_ATTR int len) { - bthc_tx((HC_BT_HDR *)transac); - return BT_HC_STATUS_SUCCESS; -} - -/** Controls HCI logging on/off */ -static int logging(bt_hc_logging_state_t state, char *p_path, bool save_existing) { - BTHCDBG("logging %d", state); - - if (state != BT_HC_LOGGING_ON) - btsnoop_close(); - else if (p_path != NULL) - btsnoop_open(p_path, save_existing); - - return BT_HC_STATUS_SUCCESS; -} - -/** sends command HC controller to configure platform specific behaviour */ -static int tx_hc_cmd(TRANSAC transac, char *p_buf, int len) { - BTHCDBG("tx_hc_cmd: transac %p", transac); - - if (!transac) - return BT_HC_STATUS_FAIL; - - thread_post(hc_cb.worker_thread, event_tx_cmd, transac); - return BT_HC_STATUS_SUCCESS; -} - -// Closes the interface. -// This routine is not thread safe. -static void cleanup(void) -{ - if (has_cleaned_up) { - ALOGW("%s Already cleaned up for this session\n", __func__); - return; - } - - BTHCDBG("cleanup"); - - hci_inject_close(); - - if (hc_cb.worker_thread) - { - if (fwcfg_acked) - { - epilog_wait_timer(); - // Stop reading thread - userial_close_reader(); - - thread_post(hc_cb.worker_thread, event_epilog, NULL); - } - thread_free(hc_cb.worker_thread); - - pthread_mutex_lock(&hc_cb.worker_thread_lock); - hc_cb.worker_thread = NULL; - pthread_mutex_unlock(&hc_cb.worker_thread_lock); - } - BTHCDBG("%s Finalizing cleanup\n", __func__); - - alarm_free(hc_cb.alarm); - hc_cb.alarm = NULL; - - lpm_cleanup(); - userial_close(); - p_hci_if->cleanup(); - utils_cleanup(); - - set_power(BT_VND_PWR_OFF); - vendor_close(); - - pthread_mutex_destroy(&hc_cb.worker_thread_lock); - - fwcfg_acked = false; - bt_hc_cbacks = NULL; - has_cleaned_up = true; -} - -static const bt_hc_interface_t bluetoothHCLibInterface = { - sizeof(bt_hc_interface_t), - init, - set_power, - lpm, - preload, - postload, - transmit_buf, - logging, - cleanup, - tx_hc_cmd, -}; - -/******************************************************************************* -** -** 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/btsnoop.c b/hci/src/btsnoop.c index e31af35f2..9df0c5dd7 100644 --- a/hci/src/btsnoop.c +++ b/hci/src/btsnoop.c @@ -32,9 +32,10 @@ #include <sys/time.h> #include <unistd.h> -#include "bt_hci_bdroid.h" +#include "btsnoop.h" +#include "bt_types.h" #include "bt_utils.h" -#include "utils.h" +#include "hci_layer.h" typedef enum { kCommandPacket = 1, @@ -105,9 +106,6 @@ static void btsnoop_write_packet(packet_type_t type, const uint8_t *packet, bool time_hi = htonl(time_hi); time_lo = htonl(time_lo); - // This function is called from different contexts. - utils_lock(); - btsnoop_write(&length, 4); btsnoop_write(&length, 4); btsnoop_write(&flags, 4); @@ -116,11 +114,9 @@ static void btsnoop_write_packet(packet_type_t type, const uint8_t *packet, bool btsnoop_write(&time_lo, 4); btsnoop_write(&type, 1); btsnoop_write(packet, length_he - 1); - - utils_unlock(); } -void btsnoop_open(const char *p_path, const bool save_existing) { +static void btsnoop_open(const char *p_path) { assert(p_path != NULL); assert(*p_path != '\0'); @@ -131,14 +127,6 @@ void btsnoop_open(const char *p_path, const bool save_existing) { return; } - if (save_existing) - { - char fname_backup[266] = {0}; - strncat(fname_backup, p_path, 255); - strcat(fname_backup, ".last"); - rename(p_path, fname_backup); - } - hci_btsnoop_fd = open(p_path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); @@ -151,7 +139,7 @@ void btsnoop_open(const char *p_path, const bool save_existing) { write(hci_btsnoop_fd, "btsnoop\0\0\0\0\1\0\0\x3\xea", 16); } -void btsnoop_close(void) { +static void btsnoop_close(void) { if (hci_btsnoop_fd != -1) close(hci_btsnoop_fd); hci_btsnoop_fd = -1; @@ -159,26 +147,36 @@ void btsnoop_close(void) { btsnoop_net_close(); } -void btsnoop_capture(const HC_BT_HDR *p_buf, bool is_rcvd) { - const uint8_t *p = (const uint8_t *)(p_buf + 1) + p_buf->offset; +static void btsnoop_capture(const BT_HDR *buffer, bool is_received) { + const uint8_t *p = buffer->data + buffer->offset; if (hci_btsnoop_fd == -1) return; - switch (p_buf->event & MSG_EVT_MASK) { + switch (buffer->event & MSG_EVT_MASK) { case MSG_HC_TO_STACK_HCI_EVT: btsnoop_write_packet(kEventPacket, p, false); break; case MSG_HC_TO_STACK_HCI_ACL: case MSG_STACK_TO_HC_HCI_ACL: - btsnoop_write_packet(kAclPacket, p, is_rcvd); + btsnoop_write_packet(kAclPacket, p, is_received); break; case MSG_HC_TO_STACK_HCI_SCO: case MSG_STACK_TO_HC_HCI_SCO: - btsnoop_write_packet(kScoPacket, p, is_rcvd); + btsnoop_write_packet(kScoPacket, p, is_received); break; case MSG_STACK_TO_HC_HCI_CMD: btsnoop_write_packet(kCommandPacket, p, true); break; } } + +static const btsnoop_interface_t interface = { + btsnoop_open, + btsnoop_close, + btsnoop_capture +}; + +const btsnoop_interface_t *btsnoop_get_interface() { + return &interface; +} diff --git a/hci/src/hci_h4.c b/hci/src/hci_h4.c deleted file mode 100644 index ba6e2a2bc..000000000 --- a/hci/src/hci_h4.c +++ /dev/null @@ -1,1062 +0,0 @@ -/****************************************************************************** - * - * 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 "btsnoop.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 -******************************************************************************/ - -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(p_buf); - 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(p_buf); - 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(p_cb->p_rcv_msg); - } - } - 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(p_return_buf); - } - 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; -} - -/******************************************************************************* -** -** Function hci_h4_cleanup -** -** Description Clean H4 module -** -** Returns None -** -*******************************************************************************/ -void hci_h4_cleanup(void) -{ - HCIDBG("hci_h4_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(p_msg); - } - 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; - - bthc_tx(p_buf); - 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(p_buf); - } - 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_hal_h4.c b/hci/src/hci_hal_h4.c new file mode 100644 index 000000000..35a939588 --- /dev/null +++ b/hci/src/hci_hal_h4.c @@ -0,0 +1,224 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * 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. + * + ******************************************************************************/ + +#define LOG_TAG "hci_hal_h4" + +#include <assert.h> +#include <utils/Log.h> + +#include "eager_reader.h" +#include "hci_hal.h" +#include "osi.h" +#include "reactor.h" +#include "vendor.h" + +#define HCI_HAL_SERIAL_BUFFER_SIZE 1026 + +// Our interface and modules we import +static const hci_hal_interface_t interface; +static const hci_hal_callbacks_t *callbacks; +static const vendor_interface_t *vendor; + +static thread_t *thread; // Not owned by us + +static int uart_fd; +static eager_reader_t *uart_stream; +static serial_data_type_t current_data_type; +static bool stream_has_interpretation; + +static void event_uart_has_bytes(eager_reader_t *reader, void *context); + +// Interface functions + +static bool hal_init(const hci_hal_callbacks_t *upper_callbacks, thread_t *upper_thread) { + assert(upper_callbacks != NULL); + assert(upper_thread != NULL); + + callbacks = upper_callbacks; + thread = upper_thread; + return true; +} + +static bool hal_open() { + // TODO(zachoverflow): close if already open / or don't reopen (maybe at the hci layer level) + + int fd_array[CH_MAX]; + int number_of_ports = vendor->send_command(VENDOR_OPEN_USERIAL, &fd_array); + + if (number_of_ports != 1) { + ALOGE("%s opened the wrong number of ports: got %d, expected 1.", __func__, number_of_ports); + goto error; + } + + uart_fd = fd_array[0]; + if (uart_fd == -1) { + ALOGE("%s unable to open the uart serial port.", __func__); + goto error; + } + + uart_stream = eager_reader_new(uart_fd, &allocator_malloc, HCI_HAL_SERIAL_BUFFER_SIZE, SIZE_MAX, "hci_single_channel"); + if (!uart_stream) { + ALOGE("%s unable to create eager reader for the uart serial port.", __func__); + goto error; + } + + stream_has_interpretation = false; + eager_reader_register(uart_stream, thread_get_reactor(thread), event_uart_has_bytes, NULL); + + return true; + +error: + interface.close(); + return false; +} + +static void hal_close() { + eager_reader_free(uart_stream); + vendor->send_command(VENDOR_CLOSE_USERIAL, NULL); +} + +static uint8_t read_byte(serial_data_type_t type) { + if (type < DATA_TYPE_ACL || type > DATA_TYPE_EVENT) { + ALOGE("%s invalid data type: %d", __func__, type); + return 0; + } else if (!stream_has_interpretation) { + ALOGE("%s read byte with no valid stream intepretation.", __func__); + return 0; + } else if (current_data_type != type) { + ALOGE("%s read byte with different type than existing interpretation.", __func__); + return 0; + } + + return eager_reader_read_byte(uart_stream); +} + +static bool has_byte(serial_data_type_t type) { + if (type < DATA_TYPE_ACL || type > DATA_TYPE_EVENT) { + ALOGE("%s invalid data type: %d", __func__, type); + return 0; + } else if (!stream_has_interpretation) { + ALOGE("%s has byte with no valid stream intepretation.", __func__); + return 0; + } else if (current_data_type != type) { + ALOGE("%s has byte with different type than existing interpretation.", __func__); + return 0; + } + + return eager_reader_has_byte(uart_stream); +} + +static void packet_finished(serial_data_type_t type) { + if (!stream_has_interpretation) + ALOGE("%s packet finished with no existing stream interpretation.", __func__); + else if (current_data_type != type) + ALOGE("%s packed finished with different type than existing interpretation.", __func__); + + stream_has_interpretation = false; +} + +static uint16_t transmit_data(serial_data_type_t type, uint8_t *data, uint16_t length) { + assert(data != NULL); + assert(length > 0); + + if (type < DATA_TYPE_COMMAND || type > DATA_TYPE_SCO) { + ALOGE("%s invalid data type: %d", __func__, type); + return 0; + } + + // Write the signal byte right before the data + --data; + uint8_t previous_byte = *data; + *(data) = type; + ++length; + + uint16_t transmitted_length = 0; + while (length > 0) { + ssize_t ret = write(uart_fd, data + transmitted_length, length); + switch (ret) { + case -1: + ALOGE("In %s, error writing to the uart serial port: %s", __func__, strerror(errno)); + goto done; + case 0: + // If we wrote nothing, don't loop more because we + // can't go to infinity or beyond + goto done; + default: + transmitted_length += ret; + length -= ret; + break; + } + } + +done:; + // Be nice and restore the old value of that byte + *(data) = previous_byte; + + // Remove the signal byte from our transmitted length, if it was actually written + if (transmitted_length > 0) + --transmitted_length; + + return transmitted_length; +} + +// Internal functions + +// See what data is waiting, and notify the upper layer +static void event_uart_has_bytes(eager_reader_t *reader, UNUSED_ATTR void *context) { + uint8_t type_byte; + + if (stream_has_interpretation) { + // Reentry in the the case that the upper layer couldn't read the entire + // packet the first time around + type_byte = current_data_type; + } + else { + type_byte = eager_reader_read_byte(reader); + if (type_byte < DATA_TYPE_ACL || type_byte > DATA_TYPE_EVENT) { + ALOGE("[h4] Unknown HCI message type. Dropping this byte 0x%x, min %x, max %x", type_byte, DATA_TYPE_ACL, DATA_TYPE_EVENT); + return; + } + + stream_has_interpretation = true; + current_data_type = type_byte; + } + + if (has_byte(current_data_type)) + callbacks->data_ready(type_byte); +} + +static const hci_hal_interface_t interface = { + hal_init, + + hal_open, + hal_close, + + read_byte, + has_byte, + packet_finished, + transmit_data, +}; + +const hci_hal_interface_t *hci_hal_get_interface() { + vendor = vendor_get_interface(); + return &interface; +} + +const hci_hal_interface_t *hci_hal_get_test_interface(vendor_interface_t *vendor_interface) { + vendor = vendor_interface; + return &interface; +} diff --git a/hci/src/hci_inject.c b/hci/src/hci_inject.c index af5ed704d..e894b73ff 100644 --- a/hci/src/hci_inject.c +++ b/hci/src/hci_inject.c @@ -22,9 +22,9 @@ #include <errno.h> #include <utils/Log.h> -#include "bt_hci_bdroid.h" #include "bt_types.h" #include "hci_inject.h" +#include "hci_layer.h" #include "list.h" #include "osi.h" #include "socket.h" @@ -45,7 +45,9 @@ typedef struct { static const port_t LISTEN_PORT = 8873; -static const bt_hc_interface_t *hci; +static const hci_inject_interface_t interface; +static const hci_interface_t *hci; +static const allocator_t *allocator; static socket_t *listen_socket; static thread_t *thread; static list_t *clients; @@ -55,12 +57,15 @@ static void accept_ready(socket_t *socket, void *context); static void read_ready(socket_t *socket, void *context); static void client_free(void *ptr); -bool hci_inject_open(void) { +bool hci_inject_open(const hci_interface_t *hci_interface, const allocator_t *buffer_allocator) { assert(listen_socket == NULL); assert(thread == NULL); assert(clients == NULL); + assert(hci_interface != NULL); + assert(buffer_allocator != NULL); - hci = bt_hc_get_interface(); + hci = hci_interface; + allocator = buffer_allocator; thread = thread_new("hci_inject"); if (!thread) @@ -81,7 +86,7 @@ bool hci_inject_open(void) { return true; error:; - hci_inject_close(); + interface.close(); return false; } @@ -136,7 +141,6 @@ static void accept_ready(socket_t *socket, UNUSED_ATTR void *context) { } static void read_ready(UNUSED_ATTR socket_t *socket, void *context) { - assert(bt_hc_cbacks != NULL); assert(socket != NULL); assert(context != NULL); @@ -162,14 +166,14 @@ static void read_ready(UNUSED_ATTR socket_t *socket, void *context) { // TODO(sharvil): once we have an HCI parser, we can eliminate // the 2-byte size field since it will be contained in the packet. - BT_HDR *buf = (BT_HDR *)bt_hc_cbacks->alloc(packet_len); + BT_HDR *buf = (BT_HDR *)allocator->alloc(packet_len); if (buf) { buf->event = hci_packet_to_event(packet_type); buf->offset = 0; buf->layer_specific = 0; buf->len = packet_len; memcpy(buf->data, buffer + 3, packet_len); - hci->transmit_buf(buf, NULL, 0); + hci->transmit_downward(buf->event, buf); } else { ALOGE("%s dropping injected packet of length %zu", __func__, packet_len); } @@ -187,3 +191,12 @@ static void client_free(void *ptr) { client_t *client = (client_t *)ptr; socket_free(client->socket); } + +static const hci_inject_interface_t interface = { + hci_inject_open, + hci_inject_close +}; + +const hci_inject_interface_t *hci_inject_get_interface() { + return &interface; +} diff --git a/hci/src/hci_layer.c b/hci/src/hci_layer.c new file mode 100644 index 000000000..ed32e84b3 --- /dev/null +++ b/hci/src/hci_layer.c @@ -0,0 +1,677 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * 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. + * + ******************************************************************************/ + +#define LOG_TAG "hci_layer" + +#include <assert.h> +#include <utils/Log.h> + +#include "alarm.h" +#include "bt_types.h" +#include "btsnoop.h" +#include "fixed_queue.h" +#include "hci_hal.h" +#include "hci_internals.h" +#include "hci_inject.h" +#include "hci_layer.h" +#include "low_power_manager.h" +#include "osi.h" +#include "packet_fragmenter.h" +#include "reactor.h" +#include "vendor.h" + +#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 + +#define INBOUND_PACKET_TYPE_COUNT 3 +#define PACKET_TYPE_TO_INBOUND_INDEX(type) ((type) - 2) +#define PACKET_TYPE_TO_INDEX(type) ((type) - 1) + +#define PREAMBLE_BUFFER_SIZE 4 // max preamble size, ACL +#define RETRIEVE_ACL_LENGTH(preamble) ((((preamble)[3]) << 8) | (preamble)[2]) + +#define MAX_WAITING_INTERNAL_COMMANDS 8 + +static const uint8_t preamble_sizes[] = { + HCI_COMMAND_PREAMBLE_SIZE, + HCI_ACL_PREAMBLE_SIZE, + HCI_SCO_PREAMBLE_SIZE, + HCI_EVENT_PREAMBLE_SIZE +}; + +static const uint16_t outbound_event_types[] = +{ + MSG_HC_TO_STACK_HCI_ERR, + MSG_HC_TO_STACK_HCI_ACL, + MSG_HC_TO_STACK_HCI_SCO, + MSG_HC_TO_STACK_HCI_EVT +}; + +typedef enum { + BRAND_NEW, + PREAMBLE, + BODY, + IGNORE, + FINISHED +} receive_state_t; + +typedef struct { + receive_state_t state; + uint16_t bytes_remaining; + uint8_t preamble[PREAMBLE_BUFFER_SIZE]; + uint16_t index; + BT_HDR *buffer; +} packet_receive_data_t; + +typedef struct { + uint16_t opcode; + internal_command_cb callback; +} waiting_internal_command_t; + +static const uint32_t EPILOG_TIMEOUT_MS = 3000; + +// Our interface +static bool interface_created; +static hci_interface_t interface; + +// Modules we import and callbacks we export +static const allocator_t *buffer_allocator; +static const btsnoop_interface_t *btsnoop; +static const hci_callbacks_t *callbacks; +static const hci_hal_interface_t *hal; +static const hci_hal_callbacks_t hal_callbacks; +static const hci_inject_interface_t *hci_inject; +static const low_power_manager_interface_t *low_power_manager; +static const packet_fragmenter_interface_t *packet_fragmenter; +static const packet_fragmenter_callbacks_t packet_fragmenter_callbacks; +static const vendor_interface_t *vendor; + +static thread_t *thread; // We own this + +static volatile bool firmware_is_configured = false; +static volatile bool has_cleaned_up = false; +static alarm_t *epilog_alarm; + +// Outbound-related +static int command_credits = 1; +static fixed_queue_t *command_queue; +static fixed_queue_t *packet_queue; + +// Inbound-related +static fixed_queue_t *waiting_internal_commands; +static packet_receive_data_t incoming_packets[INBOUND_PACKET_TYPE_COUNT]; + +static void event_preload(void *context); +static void event_postload(void *context); +static void event_epilog(void *context); +static void event_command_ready(fixed_queue_t *queue, void *context); +static void event_packet_ready(fixed_queue_t *queue, void *context); + +static void firmware_config_callback(bool success); +static void sco_config_callback(bool success); +static void epilog_finished_callback(bool success); + +static void hal_says_data_ready(serial_data_type_t type); +static bool send_internal_command(uint16_t opcode, BT_HDR *packet, internal_command_cb callback); +static void start_epilog_wait_timer(); + +// Interface functions + +static bool hci_init( + bdaddr_t local_bdaddr, + const allocator_t *upward_buffer_allocator, + const hci_callbacks_t *upper_callbacks) { + assert(local_bdaddr != NULL); + assert(upward_buffer_allocator != NULL); + assert(upper_callbacks != NULL); + + ALOGI("%s", __func__); + + // The host is only allowed to send at most one command initially, + // as per the Bluetooth spec, Volume 2, Part E, 4.4 (Command Flow Control) + // This value can change when you get a command complete or command status event. + command_credits = 1; + firmware_is_configured = false; + has_cleaned_up = false; + + epilog_alarm = alarm_new(); + if (!epilog_alarm) { + ALOGE("%s unable to create epilog alarm.", __func__); + goto error; + } + + command_queue = fixed_queue_new(SIZE_MAX); + if (!command_queue) { + ALOGE("%s unable to create pending command queue.", __func__); + goto error; + } + + packet_queue = fixed_queue_new(SIZE_MAX); + if (!packet_queue) { + ALOGE("%s unable to create pending packet queue.", __func__); + goto error; + } + + thread = thread_new("hci_thread"); + if (!thread) { + ALOGE("%s unable to create thread.", __func__); + goto error; + } + + waiting_internal_commands = fixed_queue_new(MAX_WAITING_INTERNAL_COMMANDS); + if (!waiting_internal_commands) { + ALOGE("%s unable to create waiting internal command queue.", __func__); + goto error; + } + + callbacks = upper_callbacks; + buffer_allocator = upward_buffer_allocator; + memset(incoming_packets, 0, sizeof(incoming_packets)); + + packet_fragmenter->init(&packet_fragmenter_callbacks, buffer_allocator); + + fixed_queue_register_dequeue(command_queue, thread_get_reactor(thread), event_command_ready, NULL); + fixed_queue_register_dequeue(packet_queue, thread_get_reactor(thread), event_packet_ready, NULL); + + vendor->open(local_bdaddr, buffer_allocator); + hal->init(&hal_callbacks, thread); + low_power_manager->init(thread); + + vendor->set_callback(VENDOR_CONFIGURE_FIRMWARE, firmware_config_callback); + vendor->set_callback(VENDOR_CONFIGURE_SCO, sco_config_callback); + vendor->set_callback(VENDOR_DO_EPILOG, epilog_finished_callback); + vendor->set_send_internal_command_callback(send_internal_command); + + if (!hci_inject->open(&interface, buffer_allocator)) { + // TODO(sharvil): gracefully propagate failures from this layer. + } + + return true; +error:; + interface.cleanup(); + return false; +} + +static void hci_cleanup() { + if (has_cleaned_up) { + ALOGW("%s already cleaned up for this session", __func__); + return; + } + + ALOGI("%s", __func__); + + hci_inject->close(); + + if (thread) { + if (firmware_is_configured) { + start_epilog_wait_timer(); + thread_post(thread, event_epilog, NULL); + } else { + thread_stop(thread); + } + + thread_join(thread); + } + + fixed_queue_free(command_queue, buffer_allocator->free); + fixed_queue_free(packet_queue, buffer_allocator->free); + fixed_queue_free(waiting_internal_commands, buffer_allocator->free); + + alarm_free(epilog_alarm); + epilog_alarm = NULL; + + low_power_manager->cleanup(); + hal->close(); + + interface.set_chip_power_on(false); + vendor->close(); + + thread_free(thread); + thread = NULL; + firmware_is_configured = false; + has_cleaned_up = true; +} + +static void set_chip_power_on(bool value) { + ALOGD("%s setting bluetooth chip power on to: %d", __func__, value); + + int power_state = value ? BT_VND_PWR_ON : BT_VND_PWR_OFF; + vendor->send_command(VENDOR_CHIP_POWER_CONTROL, &power_state); +} + +static void do_preload() { + ALOGD("%s posting preload work item", __func__); + thread_post(thread, event_preload, NULL); +} + +static void do_postload() { + ALOGD("%s posting postload work item", __func__); + thread_post(thread, event_postload, NULL); +} + +static void turn_on_logging(const char *path) { + ALOGD("%s", __func__); + + if (path != NULL) + btsnoop->open(path); + else + ALOGW("%s wanted to start logging, but path was NULL", __func__); +} + +static void turn_off_logging() { + ALOGD("%s", __func__); + btsnoop->close(); +} + +static void transmit_downward(data_dispatcher_type_t type, void *data) { + if (type == MSG_STACK_TO_HC_HCI_CMD) { + fixed_queue_enqueue(command_queue, data); + } else { + fixed_queue_enqueue(packet_queue, data); + } +} + +// Internal functions + +// Inspects an incoming event for interesting information, like how many +// commands are now able to be sent. Returns true if the event should +// not proceed to higher layers (i.e. was intercepted for internal use). +static bool filter_incoming_event(BT_HDR *packet) { + uint8_t *stream = packet->data; + uint8_t event_code; + + STREAM_TO_UINT8(event_code, stream); + STREAM_SKIP_UINT8(stream); // Skip the parameter total length field + + if (event_code == HCI_COMMAND_COMPLETE_EVT) { + uint16_t opcode; + + STREAM_TO_UINT8(command_credits, stream); + STREAM_TO_UINT16(opcode, stream); + + // TODO make this look back in the commands rather than just the first one + waiting_internal_command_t *first_waiting = fixed_queue_try_peek(waiting_internal_commands); + if (first_waiting != NULL && opcode == first_waiting->opcode) { + fixed_queue_dequeue(waiting_internal_commands); + + if (first_waiting->callback) + first_waiting->callback(packet); + else + buffer_allocator->free(packet); + + free(first_waiting); + return true; + } + + } else if (event_code == HCI_COMMAND_STATUS_EVT) { + STREAM_SKIP_UINT8(stream); // Skip the status field + STREAM_TO_UINT8(command_credits, stream); + } + + return false; +} + +// Send an internal command. Called by the vendor library, and also +// internally by the HCI layer to fetch controller buffer sizes. +static bool send_internal_command(uint16_t opcode, BT_HDR *packet, internal_command_cb callback) { + waiting_internal_command_t *wait_entry = (waiting_internal_command_t *)calloc(1, sizeof(waiting_internal_command_t)); + if (!wait_entry) { + ALOGE("%s couldn't allocate space for wait entry.", __func__); + return false; + } + + wait_entry->opcode = opcode; + wait_entry->callback = callback; + + if (!fixed_queue_try_enqueue(waiting_internal_commands, wait_entry)) { + free(wait_entry); + ALOGE("%s too many waiting internal commands. Rejecting 0x%04X", __func__, opcode); + return false; + } + + packet->layer_specific = opcode; + transmit_downward(packet->event, packet); + return true; +} + +static void request_acl_buffer_size_callback(void *response) { + BT_HDR *packet = (BT_HDR *)response; + uint8_t *stream = packet->data; + uint16_t opcode; + uint8_t status; + uint16_t data_size = 0; + + stream += 3; // Skip the event header fields, and the number of hci command packets field + STREAM_TO_UINT16(opcode, stream); + STREAM_TO_UINT8(status, stream); + + if (status == 0) + STREAM_TO_UINT16(data_size, stream); + + if (opcode == HCI_READ_BUFFER_SIZE) { + if (status == 0) + packet_fragmenter->set_acl_data_size(data_size); + + // Now request the ble buffer size, using the same buffer + packet->event = HCI_LE_READ_BUFFER_SIZE; + packet->offset = 0; + packet->layer_specific = 0; + packet->len = HCI_COMMAND_PREAMBLE_SIZE; + + stream = packet->data; + UINT16_TO_STREAM(stream, HCI_LE_READ_BUFFER_SIZE); + UINT8_TO_STREAM(stream, 0); // no parameters + + if (!send_internal_command(HCI_LE_READ_BUFFER_SIZE, packet, request_acl_buffer_size_callback)) { + buffer_allocator->free(packet); + ALOGI("%s couldn't send ble read buffer command, so postload finished.", __func__); + } + } else if (opcode == HCI_LE_READ_BUFFER_SIZE) { + if (status == 0) + packet_fragmenter->set_ble_acl_data_size(data_size); + + buffer_allocator->free(packet); + ALOGI("%s postload finished.", __func__); + } else { + ALOGE("%s unexpected opcode %d", __func__, opcode); + } +} + +static void request_acl_buffer_size() { + ALOGI("%s", __func__); + BT_HDR *packet = (BT_HDR *)buffer_allocator->alloc(sizeof(BT_HDR) + HCI_COMMAND_PREAMBLE_SIZE); + if (!packet) { + ALOGE("%s couldn't get buffer for packet.", __func__); + return; + } + + packet->event = MSG_STACK_TO_HC_HCI_CMD; + packet->offset = 0; + packet->layer_specific = 0; + packet->len = HCI_COMMAND_PREAMBLE_SIZE; + + uint8_t *stream = packet->data; + UINT16_TO_STREAM(stream, HCI_READ_BUFFER_SIZE); + UINT8_TO_STREAM(stream, 0); // no parameters + + if (!send_internal_command(HCI_READ_BUFFER_SIZE, packet, request_acl_buffer_size_callback)) { + buffer_allocator->free(packet); + ALOGE("%s couldn't send internal command, so postload aborted.", __func__); + } +} + +static void sco_config_callback(UNUSED_ATTR bool success) { + request_acl_buffer_size(); +} + +static void firmware_config_callback(UNUSED_ATTR bool success) { + firmware_is_configured = true; + callbacks->preload_finished(true); +} + +static void epilog_finished_callback(UNUSED_ATTR bool success) { + ALOGI("%s", __func__); + thread_stop(thread); +} + +static void epilog_wait_timer_expired(UNUSED_ATTR void *context) { + ALOGI("%s", __func__); + thread_stop(thread); +} + +static void start_epilog_wait_timer() { + alarm_set(epilog_alarm, EPILOG_TIMEOUT_MS, epilog_wait_timer_expired, NULL); +} + +static void event_preload(UNUSED_ATTR void *context) { + ALOGI("%s", __func__); + hal->open(); + vendor->send_async_command(VENDOR_CONFIGURE_FIRMWARE, NULL); +} + +static void event_postload(UNUSED_ATTR void *context) { + ALOGI("%s", __func__); + if(vendor->send_async_command(VENDOR_CONFIGURE_SCO, NULL) == -1) { + // If couldn't configure sco, we won't get the sco configuration callback + // so go pretend to do it now + sco_config_callback(false); + } +} + +static void event_epilog(UNUSED_ATTR void *context) { + vendor->send_async_command(VENDOR_DO_EPILOG, NULL); +} + +static void event_command_ready(fixed_queue_t *queue, UNUSED_ATTR void *context) { + if (command_credits > 0) { + event_packet_ready(queue, context); + } +} + +static void event_packet_ready(fixed_queue_t *queue, UNUSED_ATTR void *context) { + // The queue may be the command queue or the packet queue, we don't care + BT_HDR *packet = (BT_HDR *)fixed_queue_dequeue(queue); + + low_power_manager->wake_assert(); + packet_fragmenter->fragment_and_dispatch(packet); + low_power_manager->transmit_done(); +} + +// This function is not required to read all of a packet in one go, so +// be wary of reentry. But this function must return after finishing a packet. +static void hal_says_data_ready(serial_data_type_t type) { + packet_receive_data_t *incoming = &incoming_packets[PACKET_TYPE_TO_INBOUND_INDEX(type)]; + + while (hal->has_byte(type)) { + uint8_t byte = hal->read_byte(type); + + switch (incoming->state) { + case BRAND_NEW: + // Initialize and prepare to jump to the preamble reading state + incoming->bytes_remaining = preamble_sizes[PACKET_TYPE_TO_INDEX(type)]; + memset(incoming->preamble, 0, PREAMBLE_BUFFER_SIZE); + incoming->index = 0; + incoming->state = PREAMBLE; + // INTENTIONAL FALLTHROUGH + case PREAMBLE: + incoming->preamble[incoming->index] = byte; + incoming->index++; + incoming->bytes_remaining--; + + if (incoming->bytes_remaining == 0) { + // For event and sco preambles, the last byte we read is the length + incoming->bytes_remaining = (type == DATA_TYPE_ACL) ? RETRIEVE_ACL_LENGTH(incoming->preamble) : byte; + + size_t buffer_size = BT_HDR_SIZE + incoming->index + incoming->bytes_remaining; + incoming->buffer = (BT_HDR *)buffer_allocator->alloc(buffer_size); + + if (!incoming->buffer) { + ALOGE("%s error getting buffer for incoming packet", __func__); + // Can't read any more of this current packet, so jump out + incoming->state = incoming->bytes_remaining == 0 ? BRAND_NEW : IGNORE; + break; + } + + // Initialize the buffer + incoming->buffer->offset = 0; + incoming->buffer->layer_specific = 0; + incoming->buffer->event = outbound_event_types[PACKET_TYPE_TO_INDEX(type)]; + memcpy(incoming->buffer->data, incoming->preamble, incoming->index); + + incoming->state = incoming->bytes_remaining > 0 ? BODY : FINISHED; + } + + break; + case BODY: + incoming->buffer->data[incoming->index] = byte; + incoming->index++; + incoming->bytes_remaining--; + + // TODO(zachoverflow): consider reading in available bytes up to the length, instead of byte by byte + + incoming->state = incoming->bytes_remaining == 0 ? FINISHED : incoming->state; + break; + case IGNORE: + incoming->bytes_remaining--; + incoming->state = incoming->bytes_remaining == 0 ? BRAND_NEW : incoming->state; + break; + case FINISHED: + ALOGE("%s the state machine should not have been left in the finished state.", __func__); + break; + } + + if (incoming->state == FINISHED) { + incoming->buffer->len = incoming->index; + btsnoop->capture(incoming->buffer, true); + + if (type != DATA_TYPE_EVENT || !filter_incoming_event(incoming->buffer)) { + packet_fragmenter->reassemble_and_dispatch(incoming->buffer); + } + + // We don't control the buffer anymore + incoming->buffer = NULL; + incoming->state = BRAND_NEW; + hal->packet_finished(type); + + // We return after a packet is finished for two reasons: + // 1. The type of the next packet could be different. + // 2. We don't want to hog cpu time. + return; + } + } +} + +// TODO(zachoverflow): we seem to do this a couple places, like the HCI inject module. #centralize +static serial_data_type_t event_to_data_type(uint16_t event) { + if (event == MSG_STACK_TO_HC_HCI_ACL) + return DATA_TYPE_ACL; + else if (event == MSG_STACK_TO_HC_HCI_SCO) + return DATA_TYPE_SCO; + else if (event == MSG_STACK_TO_HC_HCI_CMD) + return DATA_TYPE_COMMAND; + else + ALOGE("%s invalid event type, could not translate.", __func__); + + return 0; +} + +// Callback for the fragmenter to send a fragment +static void transmit_fragment(BT_HDR *packet, bool send_transmit_finished) { + uint16_t opcode = 0; + uint8_t *stream = packet->data + packet->offset; + uint16_t event = packet->event & MSG_EVT_MASK; + serial_data_type_t type = event_to_data_type(event); + + if (event == MSG_STACK_TO_HC_HCI_CMD) { + command_credits--; + STREAM_TO_UINT16(opcode, stream); + } + + btsnoop->capture(packet, false); + hal->transmit_data(type, packet->data + packet->offset, packet->len); + + if (event == MSG_STACK_TO_HC_HCI_CMD + && !fixed_queue_is_empty(waiting_internal_commands) + && packet->layer_specific == opcode) { + // This is an internal command, so nobody owns the buffer now + buffer_allocator->free(packet); + } else if (send_transmit_finished) { + callbacks->transmit_finished(packet, true); + } +} + +// Callback for the fragmenter to dispatch up a completely reassembled packet +static void dispatch_reassembled(BT_HDR *packet) { + data_dispatcher_dispatch( + interface.upward_dispatcher, + packet->event & MSG_EVT_MASK, + packet + ); +} + +static void fragmenter_transmit_finished(void *buffer, bool all_fragments_sent) { + callbacks->transmit_finished(buffer, all_fragments_sent); +} + +static void init_layer_interface() { + if (!interface_created) { + interface.init = hci_init; + interface.cleanup = hci_cleanup; + + interface.set_chip_power_on = set_chip_power_on; + interface.send_low_power_command = low_power_manager->post_command; + interface.do_preload = do_preload; + interface.do_postload = do_postload; + interface.turn_on_logging = turn_on_logging; + interface.turn_off_logging = turn_off_logging; + + // It's probably ok for this to live forever. It's small and + // there's only one instance of the hci interface. + interface.upward_dispatcher = data_dispatcher_new("hci_layer"); + if (!interface.upward_dispatcher) { + ALOGE("%s could not create upward dispatcher.", __func__); + return; + } + + interface.transmit_downward = transmit_downward; + interface_created = true; + } +} + +static const hci_hal_callbacks_t hal_callbacks = { + hal_says_data_ready +}; + +static const packet_fragmenter_callbacks_t packet_fragmenter_callbacks = { + transmit_fragment, + dispatch_reassembled, + fragmenter_transmit_finished +}; + +const hci_interface_t *hci_layer_get_interface() { + hal = hci_hal_get_interface(); + btsnoop = btsnoop_get_interface(); + hci_inject = hci_inject_get_interface(); + packet_fragmenter = packet_fragmenter_get_interface(); + vendor = vendor_get_interface(); + low_power_manager = low_power_manager_get_interface(); + + init_layer_interface(); + return &interface; +} + +const hci_interface_t *hci_layer_get_test_interface( + const hci_hal_interface_t *hal_interface, + const btsnoop_interface_t *btsnoop_interface, + const hci_inject_interface_t *hci_inject_interface, + const packet_fragmenter_interface_t *packet_fragmenter_interface, + const vendor_interface_t *vendor_interface, + const low_power_manager_interface_t *low_power_manager_interface) { + + hal = hal_interface; + btsnoop = btsnoop_interface; + hci_inject = hci_inject_interface; + packet_fragmenter = packet_fragmenter_interface; + vendor = vendor_interface; + low_power_manager = low_power_manager_interface; + + init_layer_interface(); + return &interface; +} diff --git a/hci/src/low_power_manager.c b/hci/src/low_power_manager.c new file mode 100644 index 000000000..db09d50f9 --- /dev/null +++ b/hci/src/low_power_manager.c @@ -0,0 +1,242 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * 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. + * + ******************************************************************************/ + +#define LOG_TAG "bt_low_power_manager" + +#include <assert.h> +#include <stdint.h> +#include <utils/Log.h> + +#include "alarm.h" +#include "low_power_manager.h" +#include "osi.h" +#include "thread.h" +#include "vendor.h" + +typedef enum { + LPM_DISABLED = 0, + LPM_ENABLED, + LPM_ENABLING, + LPM_DISABLING +} low_power_mode_state_t; + +typedef enum { + LPM_WAKE_DEASSERTED = 0, + LPM_WAKE_W4_TX_DONE, + LPM_WAKE_W4_TIMEOUT, + LPM_WAKE_ASSERTED, +} wake_state_t; + +// Our interface and modules we import +static const low_power_manager_interface_t interface; +static const vendor_interface_t *vendor; + +static void vendor_enable_disable_callback(bool success); + +static void event_disable(void *context); +static void event_enable(void *context); +static void event_wake_assert(void *context); +static void event_allow_device_sleep(void *context); +static void event_idle_timeout(void *context); + +static void reset_state(); +static void start_idle_timer(); +static void stop_idle_timer(); + +static thread_fn event_functions[] = { + event_disable, + event_enable, + event_wake_assert, + event_allow_device_sleep +}; + +static thread_t *thread; +static low_power_mode_state_t state; +static wake_state_t wake_state; +static uint32_t idle_timeout_ms; +static alarm_t *idle_alarm; +static bool transmit_is_done; + +// Interface functions + +static void init(thread_t *post_thread) { + assert(post_thread != NULL); + thread = post_thread; + + vendor->set_callback(VENDOR_SET_LPM_MODE, vendor_enable_disable_callback); + vendor->send_command(VENDOR_GET_LPM_IDLE_TIMEOUT, &idle_timeout_ms); + + idle_alarm = alarm_new(); + if (!idle_alarm) { + ALOGE("%s could not create idle alarm.", __func__); + } + + reset_state(); +} + +static void cleanup() { + reset_state(); + alarm_free(idle_alarm); + idle_alarm = NULL; +} + +static void post_command(low_power_command_t command) { + if (command > LPM_WAKE_DEASSERT) { + ALOGE("%s unknown low power command %d", __func__, command); + return; + } + + thread_post(thread, event_functions[command], NULL); +} + +static void wake_assert() { + if (state != LPM_DISABLED) { + stop_idle_timer(); + + uint8_t new_state = BT_VND_LPM_WAKE_ASSERT; + vendor->send_command(VENDOR_SET_LPM_WAKE_STATE, &new_state); + wake_state = LPM_WAKE_ASSERTED; + } + + // TODO(zachoverflow): investigate this interaction. If someone above + // HCI asserts wake, we'll wait until we transmit before deasserting. + // That doesn't seem quite right. + transmit_is_done = false; +} + +static void transmit_done() { + transmit_is_done = true; + if (wake_state == LPM_WAKE_W4_TX_DONE) { + wake_state = LPM_WAKE_W4_TIMEOUT; + start_idle_timer(); + } +} + +// Internal functions + +static void enable(bool enable) { + if (state == LPM_DISABLING) { + if (enable) + ALOGE("%s still processing prior disable request, cannot enable.", __func__); + else + ALOGW("%s still processing prior disable request, ignoring new request to disable.", __func__); + } else if (state == LPM_ENABLING) { + if (enable) + ALOGE("%s still processing prior enable request, ignoring new request to enable.", __func__); + else + ALOGW("%s still processing prior enable request, cannot disable.", __func__); + } else if (state == LPM_ENABLED && enable) { + ALOGI("%s already enabled.", __func__); + } else if (state == LPM_DISABLED && !enable) { + ALOGI("%s already disabled.", __func__); + } else { + uint8_t command = enable ? BT_VND_LPM_ENABLE : BT_VND_LPM_DISABLE; + state = enable ? LPM_ENABLING : LPM_DISABLING; + vendor->send_async_command(VENDOR_SET_LPM_MODE, &command); + } +} + +static void allow_device_sleep() { + if (state == LPM_ENABLED && wake_state == LPM_WAKE_ASSERTED) { + if (transmit_is_done) { + wake_state = LPM_WAKE_W4_TIMEOUT; + start_idle_timer(); + } else { + wake_state = LPM_WAKE_W4_TX_DONE; + } + } +} + +static void wake_deassert() { + if (state == LPM_ENABLED && transmit_is_done) { + uint8_t new_state = BT_VND_LPM_WAKE_DEASSERT; + vendor->send_command(VENDOR_SET_LPM_WAKE_STATE, &new_state); + wake_state = LPM_WAKE_DEASSERTED; + } +} + +static void reset_state() { + state = LPM_DISABLED; + wake_state = LPM_WAKE_DEASSERTED; + transmit_is_done = true; + stop_idle_timer(); +} + +static void idle_timer_expired(UNUSED_ATTR void *context) { + if (state == LPM_ENABLED && wake_state == LPM_WAKE_W4_TIMEOUT) + thread_post(thread, event_idle_timeout, NULL); +} + +static void start_idle_timer() { + if (state == LPM_ENABLED) { + alarm_set(idle_alarm, idle_timeout_ms, idle_timer_expired, NULL); + } +} + +static void stop_idle_timer() { + alarm_cancel(idle_alarm); +} + +static void event_disable(UNUSED_ATTR void *context) { + enable(false); +} + +static void event_enable(UNUSED_ATTR void *context) { + enable(true); +} + +static void event_wake_assert(UNUSED_ATTR void *context) { + wake_assert(); +} + +static void event_allow_device_sleep(UNUSED_ATTR void *context) { + allow_device_sleep(); +} + +static void event_idle_timeout(UNUSED_ATTR void *context) { + wake_deassert(); +} + +static void vendor_enable_disable_callback(bool success) { + if (success) + state = (state == LPM_ENABLING) ? LPM_ENABLED : LPM_DISABLED; + else + state = (state == LPM_ENABLING) ? LPM_DISABLED : LPM_ENABLED; + + if (state == LPM_DISABLED) { + reset_state(); + } +} + +static const low_power_manager_interface_t interface = { + init, + cleanup, + post_command, + wake_assert, + transmit_done +}; + +const low_power_manager_interface_t *low_power_manager_get_interface() { + vendor = vendor_get_interface(); + return &interface; +} + +const low_power_manager_interface_t *low_power_manager_get_test_interface(const vendor_interface_t *vendor_interface) { + vendor = vendor_interface; + return &interface; +} diff --git a/hci/src/lpm.c b/hci/src/lpm.c deleted file mode 100644 index d8c7e363f..000000000 --- a/hci/src/lpm.c +++ /dev/null @@ -1,362 +0,0 @@ -/****************************************************************************** - * - * 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 "alarm.h" -#include "bt_hci_bdroid.h" -#include "bt_vendor_lib.h" -#include "bt_utils.h" -#include "osi.h" -#include "vendor.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 -******************************************************************************/ - -/****************************************************************************** -** 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; - alarm_t *alarm; - 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(UNUSED_ATTR void *context) -{ - BTLPMDBG("..lpm_idle_timeout.."); - - if ((bt_lpm_cb.state == LPM_ENABLED) && - (bt_lpm_cb.wake_state == LPM_WAKE_W4_TIMEOUT)) - { - bthc_idle_timeout(); - } -} - -/******************************************************************************* -** -** Function lpm_start_transport_idle_timer -** -** Description Launch transport idle timer -** -** Returns None -** -*******************************************************************************/ -static void lpm_start_transport_idle_timer(void) -{ - if (bt_lpm_cb.state != LPM_ENABLED) - return; - - alarm_set(bt_lpm_cb.alarm, bt_lpm_cb.timeout_ms, lpm_idle_timeout, NULL); -} - -/******************************************************************************* -** -** Function lpm_stop_transport_idle_timer -** -** Description Launch transport idle timer -** -** Returns None -** -*******************************************************************************/ -static void lpm_stop_transport_idle_timer(void) -{ - alarm_cancel(bt_lpm_cb.alarm); -} - -/******************************************************************************* -** -** 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) - { - alarm_free(bt_lpm_cb.alarm); - 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)); - vendor_send_command(BT_VND_OP_GET_LPM_IDLE_TIMEOUT, &bt_lpm_cb.timeout_ms); - bt_lpm_cb.alarm = alarm_new(); -} - -/******************************************************************************* -** -** Function lpm_cleanup -** -** Description Clean up -** -** Returns None -** -*******************************************************************************/ -void lpm_cleanup(void) -{ - alarm_free(bt_lpm_cb.alarm); - bt_lpm_cb.alarm = NULL; -} - -/******************************************************************************* -** -** 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); - } - - uint8_t lpm_cmd = (turn_on) ? BT_VND_LPM_ENABLE : BT_VND_LPM_DISABLE; - bt_lpm_cb.state = (turn_on) ? LPM_ENABLING : LPM_DISABLING; - vendor_send_command(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 */ - uint8_t state = BT_VND_LPM_WAKE_ASSERT; - vendor_send_command(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"); - - uint8_t state = BT_VND_LPM_WAKE_DEASSERT; - vendor_send_command(BT_VND_OP_LPM_WAKE_SET_STATE, &state); - bt_lpm_cb.wake_state = LPM_WAKE_DEASSERTED; - } -} diff --git a/hci/src/packet_fragmenter.c b/hci/src/packet_fragmenter.c new file mode 100644 index 000000000..7e018a1ea --- /dev/null +++ b/hci/src/packet_fragmenter.c @@ -0,0 +1,232 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * 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. + * + ******************************************************************************/ + +#define LOG_TAG "hci_hal_h4" + +#include <assert.h> +#include <utils/Log.h> + +#include "hash_map.h" +#include "hci_internals.h" +#include "hci_layer.h" +#include "packet_fragmenter.h" +#include "osi.h" + +#define APPLY_CONTINUATION_FLAG(handle) (((handle) & 0xCFFF) | 0x1000) +#define APPLY_START_FLAG(handle) (((handle) & 0xCFFF) | 0x2000) +#define SUB_EVENT(event) ((event) & MSG_SUB_EVT_MASK) +#define GET_BOUNDARY_FLAG(handle) (((handle) >> 12) & 0x0003) + +#define HANDLE_MASK 0x0FFF +#define START_PACKET_BOUNDARY 2 +#define CONTINUATION_PACKET_BOUNDARY 1 +#define L2CAP_HEADER_SIZE 4 + +// TODO(zachoverflow): find good value for this +#define NUMBER_OF_BUCKETS 42 + +// Our interface and callbacks +static const packet_fragmenter_interface_t interface; +static const allocator_t *allocator; +static const packet_fragmenter_callbacks_t *callbacks; + +static uint16_t acl_data_size; +static uint16_t ble_acl_data_size; +static hash_map_t *partial_packets; + +static hash_index_t default_hash_function(const void *key); + +static void init(const packet_fragmenter_callbacks_t *result_callbacks, const allocator_t *buffer_allocator) { + allocator = buffer_allocator; + callbacks = result_callbacks; + + // Give initial values for the data sizes, which will + // be updated when we talk to the actual controller + acl_data_size = 1021; + ble_acl_data_size = 27; + + if (partial_packets) + hash_map_free(partial_packets); + + partial_packets = hash_map_new(NUMBER_OF_BUCKETS, default_hash_function, NULL, NULL); +} + +static void set_acl_data_size(uint16_t size) { + assert(size > 0); + acl_data_size = size; +} + +static void set_ble_acl_data_size(uint16_t size) { + // If the ble acl data size is zero, the ble acl buffers are the same size + // as the normal ones. + ble_acl_data_size = size == 0 ? acl_data_size : size; +} + +static void fragment_and_dispatch(BT_HDR *packet) { + assert(packet != NULL); + + uint16_t remaining_length = packet->len; + uint16_t event = packet->event & MSG_EVT_MASK; + uint16_t max_data_size = SUB_EVENT(packet->event) == LOCAL_BR_EDR_CONTROLLER_ID ? acl_data_size : ble_acl_data_size; + uint16_t max_packet_size = max_data_size + HCI_ACL_PREAMBLE_SIZE; + + uint8_t *stream = packet->data + packet->offset; + + if (event == MSG_STACK_TO_HC_HCI_ACL && remaining_length > max_packet_size) { + uint16_t continuation_handle; + STREAM_TO_UINT16(continuation_handle, stream); + continuation_handle = APPLY_CONTINUATION_FLAG(continuation_handle); + + while (remaining_length > max_packet_size) { + // Make sure we use the right ACL packet size + stream = packet->data + packet->offset; + STREAM_SKIP_UINT16(stream); + UINT16_TO_STREAM(stream, max_data_size); + + packet->len = max_packet_size; + callbacks->fragmented(packet, false); + + packet->offset += max_data_size; + remaining_length -= max_data_size; + packet->len = remaining_length; + + // Write the ACL header for the next fragment + stream = packet->data + packet->offset; + UINT16_TO_STREAM(stream, continuation_handle); + UINT16_TO_STREAM(stream, remaining_length - HCI_ACL_PREAMBLE_SIZE); + + // Apparently L2CAP can set layer_specific to a max number of segments to transmit + if (packet->layer_specific) { + packet->layer_specific--; + + if (packet->layer_specific == 0) { + packet->event = MSG_HC_TO_STACK_L2C_SEG_XMIT; + callbacks->transmit_finished(packet, false); + return; + } + } + } + } + + callbacks->fragmented(packet, true); +} + +static void reassemble_and_dispatch(UNUSED_ATTR BT_HDR *packet) { + if ((packet->event & MSG_EVT_MASK) == MSG_HC_TO_STACK_HCI_ACL) { + uint8_t *stream = packet->data; + uint16_t handle; + uint16_t l2cap_length; + uint16_t acl_length; + + STREAM_TO_UINT16(handle, stream); + STREAM_TO_UINT16(acl_length, stream); + STREAM_TO_UINT16(l2cap_length, stream); + + assert(acl_length == packet->len - HCI_ACL_PREAMBLE_SIZE); + + uint8_t boundary_flag = GET_BOUNDARY_FLAG(handle); + handle = handle & HANDLE_MASK; + + BT_HDR *partial_packet = (BT_HDR *)hash_map_get(partial_packets, (void *)(uintptr_t)handle); + + if (boundary_flag == START_PACKET_BOUNDARY) { + if (partial_packet) { + ALOGW("%s found unfinished packet for handle with start packet. Dropping old.", __func__); + + hash_map_erase(partial_packets, (void *)(uintptr_t)handle); + allocator->free(partial_packet); + } + + uint16_t full_length = l2cap_length + L2CAP_HEADER_SIZE + HCI_ACL_PREAMBLE_SIZE; + if (full_length <= packet->len) { + if (full_length < packet->len) + ALOGW("%s found l2cap full length %d less than the hci length %d.", __func__, l2cap_length, packet->len); + + callbacks->reassembled(packet); + return; + } + + partial_packet = (BT_HDR *)allocator->alloc(full_length + sizeof(BT_HDR)); + partial_packet->event = packet->event; + partial_packet->len = full_length; + partial_packet->offset = packet->len; + + memcpy(partial_packet->data, packet->data, packet->len); + + // Update the ACL data size to indicate the full expected length + stream = partial_packet->data; + STREAM_SKIP_UINT16(stream); // skip the handle + UINT16_TO_STREAM(stream, full_length - HCI_ACL_PREAMBLE_SIZE); + + hash_map_set(partial_packets, (void *)(uintptr_t)handle, partial_packet); + // Free the old packet buffer, since we don't need it anymore + allocator->free(packet); + } else { + if (!partial_packet) { + ALOGW("%s got continuation for unknown packet. Dropping it.", __func__); + allocator->free(packet); + return; + } + + packet->offset = HCI_ACL_PREAMBLE_SIZE; + uint16_t projected_offset = partial_packet->offset + (packet->len - HCI_ACL_PREAMBLE_SIZE); + if (projected_offset > partial_packet->len) { // len stores the expected length + ALOGW("%s got packet which would exceed expected length of %d. Truncating.", __func__, partial_packet->len); + packet->len = partial_packet->len - partial_packet->offset; + projected_offset = partial_packet->len; + } + + memcpy( + partial_packet->data + partial_packet->offset, + packet->data + packet->offset, + packet->len - packet->offset + ); + + // Free the old packet buffer, since we don't need it anymore + allocator->free(packet); + partial_packet->offset = projected_offset; + + if (partial_packet->offset == partial_packet->len) { + hash_map_erase(partial_packets, (void *)(uintptr_t)handle); + partial_packet->offset = 0; + callbacks->reassembled(partial_packet); + } + } + } else { + callbacks->reassembled(packet); + } +} + +static hash_index_t default_hash_function(const void *key) { + hash_index_t hash_key = (hash_index_t)key; + return hash_key; +} + +static const packet_fragmenter_interface_t interface = { + init, + + set_acl_data_size, + set_ble_acl_data_size, + + fragment_and_dispatch, + reassemble_and_dispatch +}; + +const packet_fragmenter_interface_t *packet_fragmenter_get_interface() { + return &interface; +} diff --git a/hci/src/userial.c b/hci/src/userial.c deleted file mode 100644 index e3f35578c..000000000 --- a/hci/src/userial.c +++ /dev/null @@ -1,418 +0,0 @@ -/****************************************************************************** - * - * 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 <assert.h> -#include <errno.h> -#include <fcntl.h> -#include <pthread.h> -#include <stdio.h> -#include <sys/eventfd.h> -#include <sys/prctl.h> -#include <sys/socket.h> -#include <utils/Log.h> - -#include "bt_hci_bdroid.h" -#include "bt_utils.h" -#include "bt_vendor_lib.h" -#include "userial.h" -#include "utils.h" -#include "vendor.h" - -/****************************************************************************** -** Constants & Macros -******************************************************************************/ - -#ifndef USERIAL_DBG -#define USERIAL_DBG TRUE -#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) - -// The set of events one can send to the userial read thread. -// Note that the values must be >= 0x8000000000000000 to guarantee delivery -// of the message (see eventfd(2) for details on blocking behaviour). -enum { - USERIAL_RX_EXIT = 0x8000000000000000ULL -}; - -/****************************************************************************** -** Externs -******************************************************************************/ - -/****************************************************************************** -** 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; - -/***************************************************************************** -** 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 event_fd = -1; - -static inline int add_event_fd(fd_set *set) { - if (event_fd == -1) { - event_fd = eventfd(0, 0); - if (event_fd == -1) { - ALOGE("%s unable to create event fd: %s", __func__, strerror(errno)); - return -1; - } - } - - FD_SET(event_fd, set); - return event_fd; -} - -static inline void send_event(uint64_t event_id) { - assert(event_fd != -1); - eventfd_write(event_fd, event_id); -} - -static inline uint64_t read_event() { - assert(event_fd != -1); - - uint64_t value = 0; - eventfd_read(event_fd, &value); - return value; -} - -static inline bool is_event_available(fd_set *set) { - assert(event_fd != -1); - return !!FD_ISSET(event_fd, 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; - - while (userial_running) - { - /* Initialize the input fd set */ - FD_ZERO(&input); - FD_SET(fd, &input); - int fd_max = add_event_fd(&input); - fd_max = fd_max > fd ? fd_max : fd; - - /* Do the select */ - n = select(fd_max+1, &input, NULL, NULL, NULL); - if(is_event_available(&input)) - { - uint64_t event = read_event(); - switch (event) { - case USERIAL_RX_EXIT: - USERIALDBG("RX termination"); - return -1; - } - } - - 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; -} - -static void *userial_read_thread(void *arg) -{ - int rx_length = 0; - HC_BT_HDR *p_buf = NULL; - uint8_t *p; - UNUSED(arg); - - USERIALDBG("Entering userial_read_thread()"); - prctl(PR_SET_NAME, (unsigned long)"userial_read", 0, 0, 0); - - userial_running = 1; - - raise_priority_a2dp(TASK_HIGH_USERIAL_READ); - - while (userial_running) - { - if (bt_hc_cbacks) - { - p_buf = (HC_BT_HDR *) bt_hc_cbacks->alloc( - BT_HC_HDR_SIZE + HCI_MAX_FRAME_SIZE + 1); /* H4 HDR = 1 */ - } - else - p_buf = NULL; - - if (p_buf != NULL) - { - p_buf->offset = 0; - p_buf->layer_specific = 0; - - p = (uint8_t *) (p_buf + 1); - int userial_fd = userial_cb.fd; - if (userial_fd != -1) - rx_length = select_read(userial_fd, p, HCI_MAX_FRAME_SIZE + 1); - else - rx_length = 0; - } - 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_rx_ready(); - } - 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(p_buf); - /* 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 -*****************************************************************************/ - -bool 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; -} - -bool userial_open(userial_port_t port) { - if (port >= MAX_SERIAL_PORT) { - ALOGE("%s serial port %d > %d (max).", __func__, port, MAX_SERIAL_PORT); - return false; - } - - if (userial_running) { - userial_close(); - utils_delay(50); - } - - // Call in to the vendor-specific library to open the serial port. - int fd_array[CH_MAX]; - for (int i = 0; i < CH_MAX; i++) - fd_array[i] = -1; - - int num_ports = vendor_send_command(BT_VND_OP_USERIAL_OPEN, &fd_array); - - if (num_ports != 1) { - ALOGE("%s opened wrong number of ports: got %d, expected 1.", __func__, num_ports); - goto error; - } - - userial_cb.fd = fd_array[0]; - if (userial_cb.fd == -1) { - ALOGE("%s unable to open serial port.", __func__); - goto error; - } - - userial_cb.port = port; - - if (pthread_create(&userial_cb.read_thread, NULL, userial_read_thread, NULL)) { - ALOGE("%s unable to spawn read thread.", __func__); - goto error; - } - - return true; - -error: - vendor_send_command(BT_VND_OP_USERIAL_CLOSE, NULL); - return false; -} - -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; - UNUSED(msg_id); - - 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(userial_cb.p_rx_hdr); - - 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; -} - -uint16_t userial_write(uint16_t msg_id, const uint8_t *p_data, uint16_t len) { - UNUSED(msg_id); - - uint16_t total = 0; - while (len) { - ssize_t ret = write(userial_cb.fd, p_data + total, len); - switch (ret) { - case -1: - ALOGE("%s error writing to serial port: %s", __func__, strerror(errno)); - return total; - case 0: // don't loop forever in case write returns 0. - return total; - default: - total += ret; - len -= ret; - break; - } - } - - return total; -} - -void userial_close_reader(void) { - // Join the reader thread if it is still running. - if (userial_running) { - send_event(USERIAL_RX_EXIT); - int result = pthread_join(userial_cb.read_thread, NULL); - USERIALDBG("%s Joined userial reader thread: %d", __func__, result); - if (result) - ALOGE("%s failed to join reader thread: %d", __func__, result); - return; - } - ALOGW("%s Already closed userial reader thread", __func__); -} - -void userial_close(void) { - assert(bt_hc_cbacks != NULL); - - // Join the reader thread if it's still running. - if (userial_running) { - send_event(USERIAL_RX_EXIT); - int result = pthread_join(userial_cb.read_thread, NULL); - if (result) - ALOGE("%s failed to join reader thread: %d", __func__, result); - } - - // Ask the vendor-specific library to close the serial port. - vendor_send_command(BT_VND_OP_USERIAL_CLOSE, NULL); - - // Free all buffers still waiting in the RX queue. - // TODO: use list data structure and clean this up. - void *buf; - while ((buf = utils_dequeue(&userial_cb.rx_q)) != NULL) - bt_hc_cbacks->dealloc(buf); - - userial_cb.fd = -1; -} diff --git a/hci/src/utils.c b/hci/src/utils.c deleted file mode 100644 index 85304fa81..000000000 --- a/hci/src/utils.c +++ /dev/null @@ -1,309 +0,0 @@ -/****************************************************************************** - * - * 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) -{ - pthread_mutex_destroy(&utils_mutex); -} - -/******************************************************************************* -** -** 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); -} - diff --git a/hci/src/vendor.c b/hci/src/vendor.c index e6627a280..f287e12cb 100644 --- a/hci/src/vendor.c +++ b/hci/src/vendor.c @@ -22,45 +22,29 @@ #include <dlfcn.h> #include <utils/Log.h> -#include "bt_hci_bdroid.h" #include "bt_vendor_lib.h" -#include "hci.h" #include "osi.h" +#include "vendor.h" -// TODO: eliminate these three. -extern tHCI_IF *p_hci_if; -extern bool fwcfg_acked; -void lpm_vnd_cback(uint8_t vnd_result); +#define LAST_VENDOR_OPCODE_VALUE VENDOR_DO_EPILOG static const char *VENDOR_LIBRARY_NAME = "libbt-vendor.so"; static const char *VENDOR_LIBRARY_SYMBOL_NAME = "BLUETOOTH_VENDOR_LIB_INTERFACE"; +static const vendor_interface_t interface; +static const allocator_t *allocator; +static vendor_cb callbacks[LAST_VENDOR_OPCODE_VALUE + 1]; +static send_internal_command_cb send_internal_command_callback; + static void *lib_handle; -static bt_vendor_interface_t *vendor_interface; - -static void firmware_config_cb(bt_vendor_op_result_t result); -static void sco_config_cb(bt_vendor_op_result_t result); -static void low_power_mode_cb(bt_vendor_op_result_t result); -static void sco_audiostate_cb(bt_vendor_op_result_t result); -static void *buffer_alloc_cb(int size); -static void buffer_free_cb(void *buffer); -static uint8_t transmit_cb(uint16_t opcode, void *buffer, tINT_CMD_CBACK callback); -static void epilog_cb(bt_vendor_op_result_t result); - -static const bt_vendor_callbacks_t vendor_callbacks = { - sizeof(vendor_callbacks), - firmware_config_cb, - sco_config_cb, - low_power_mode_cb, - sco_audiostate_cb, - buffer_alloc_cb, - buffer_free_cb, - transmit_cb, - epilog_cb -}; +static bt_vendor_interface_t *lib_interface; +static const bt_vendor_callbacks_t lib_callbacks; + +// Interface functions -bool vendor_open(const uint8_t *local_bdaddr) { +static bool vendor_open(const uint8_t *local_bdaddr, const allocator_t *buffer_allocator) { assert(lib_handle == NULL); + allocator = buffer_allocator; lib_handle = dlopen(VENDOR_LIBRARY_NAME, RTLD_NOW); if (!lib_handle) { @@ -68,13 +52,15 @@ bool vendor_open(const uint8_t *local_bdaddr) { goto error; } - vendor_interface = (bt_vendor_interface_t *)dlsym(lib_handle, VENDOR_LIBRARY_SYMBOL_NAME); - if (!vendor_interface) { + lib_interface = (bt_vendor_interface_t *)dlsym(lib_handle, VENDOR_LIBRARY_SYMBOL_NAME); + if (!lib_interface) { ALOGE("%s unable to find symbol %s in %s: %s", __func__, VENDOR_LIBRARY_SYMBOL_NAME, VENDOR_LIBRARY_NAME, dlerror()); goto error; } - int status = vendor_interface->init(&vendor_callbacks, (unsigned char *)local_bdaddr); + ALOGI("alloc value %p", lib_callbacks.alloc); + + int status = lib_interface->init(&lib_callbacks, (unsigned char *)local_bdaddr); if (status) { ALOGE("%s unable to initialize vendor library: %d", __func__, status); goto error; @@ -83,57 +69,70 @@ bool vendor_open(const uint8_t *local_bdaddr) { return true; error:; - vendor_interface = NULL; + lib_interface = NULL; if (lib_handle) dlclose(lib_handle); lib_handle = NULL; return false; } -void vendor_close(void) { - if (vendor_interface) - vendor_interface->cleanup(); +static void vendor_close(void) { + if (lib_interface) + lib_interface->cleanup(); if (lib_handle) dlclose(lib_handle); - vendor_interface = NULL; + lib_interface = NULL; lib_handle = NULL; } -int vendor_send_command(bt_vendor_opcode_t opcode, void *param) { - assert(vendor_interface != NULL); +static int send_command(vendor_opcode_t opcode, void *param) { + assert(lib_interface != NULL); + return lib_interface->op(opcode, param); +} - return vendor_interface->op(opcode, param); +static int send_async_command(vendor_async_opcode_t opcode, void *param) { + assert(lib_interface != NULL); + return lib_interface->op(opcode, param); } +static void set_callback(vendor_async_opcode_t opcode, vendor_cb callback) { + callbacks[opcode] = callback; +} + +static void set_send_internal_command_callback(send_internal_command_cb callback) { + send_internal_command_callback = callback; +} + +// Internal functions + // Called back from vendor library when the firmware configuration // completes. static void firmware_config_cb(bt_vendor_op_result_t result) { - assert(bt_hc_cbacks != NULL); - - fwcfg_acked = true; - - bt_hc_postload_result_t status = (result == BT_VND_OP_RESULT_SUCCESS) - ? BT_HC_PRELOAD_SUCCESS - : BT_HC_PRELOAD_FAIL; - bt_hc_cbacks->preload_cb(NULL, status); + ALOGI("firmware callback"); + vendor_cb callback = callbacks[VENDOR_CONFIGURE_FIRMWARE]; + assert(callback != NULL); + callback(result == BT_VND_OP_RESULT_SUCCESS); } // Called back from vendor library to indicate status of previous // SCO configuration request. This should only happen during the // postload process. -static void sco_config_cb(UNUSED_ATTR bt_vendor_op_result_t result) { - assert(p_hci_if != NULL); - - // Continue the rest of the postload process. - p_hci_if->get_acl_max_len(); +static void sco_config_cb(bt_vendor_op_result_t result) { + ALOGI("%s", __func__); + vendor_cb callback = callbacks[VENDOR_CONFIGURE_SCO]; + assert(callback != NULL); + callback(result == BT_VND_OP_RESULT_SUCCESS); } // Called back from vendor library to indicate status of previous // LPM enable/disable request. static void low_power_mode_cb(bt_vendor_op_result_t result) { - lpm_vnd_cback(result != BT_VND_OP_RESULT_SUCCESS); + ALOGI("%s", __func__); + vendor_cb callback = callbacks[VENDOR_SET_LPM_MODE]; + assert(callback != NULL); + callback(result == BT_VND_OP_RESULT_SUCCESS); } /****************************************************************************** @@ -156,25 +155,52 @@ static void sco_audiostate_cb(bt_vendor_op_result_t result) // Called by vendor library when it needs an HCI buffer. static void *buffer_alloc_cb(int size) { - assert(bt_hc_cbacks != NULL); - return bt_hc_cbacks->alloc(size); + return allocator->alloc(size); } // Called by vendor library when it needs to free a buffer allocated with // |buffer_alloc_cb|. static void buffer_free_cb(void *buffer) { - assert(bt_hc_cbacks != NULL); - bt_hc_cbacks->dealloc(buffer); + allocator->free(buffer); } // Called back from vendor library when it wants to send an HCI command. static uint8_t transmit_cb(uint16_t opcode, void *buffer, tINT_CMD_CBACK callback) { - assert(p_hci_if != NULL); - return p_hci_if->send_int_cmd(opcode, (HC_BT_HDR *)buffer, callback); + assert(send_internal_command_callback != NULL); + return send_internal_command_callback(opcode, (BT_HDR *)buffer, callback); } // Called back from vendor library when the epilog procedure has // completed. It is safe to call vendor_interface->cleanup() after // this callback has been received. -static void epilog_cb(UNUSED_ATTR bt_vendor_op_result_t result) { +static void epilog_cb(bt_vendor_op_result_t result) { + ALOGI("%s", __func__); + vendor_cb callback = callbacks[VENDOR_DO_EPILOG]; + assert(callback != NULL); + callback(result == BT_VND_OP_RESULT_SUCCESS); +} + +static const bt_vendor_callbacks_t lib_callbacks = { + sizeof(lib_callbacks), + firmware_config_cb, + sco_config_cb, + low_power_mode_cb, + sco_audiostate_cb, + buffer_alloc_cb, + buffer_free_cb, + transmit_cb, + epilog_cb +}; + +static const vendor_interface_t interface = { + vendor_open, + vendor_close, + send_command, + send_async_command, + set_callback, + set_send_internal_command_callback +}; + +const vendor_interface_t *vendor_get_interface() { + return &interface; } diff --git a/hci/test/hci_hal_h4_test.cpp b/hci/test/hci_hal_h4_test.cpp new file mode 100644 index 000000000..582cda046 --- /dev/null +++ b/hci/test/hci_hal_h4_test.cpp @@ -0,0 +1,259 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * 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. + * + ******************************************************************************/ + +#include <gtest/gtest.h> + +extern "C" { +#include <stdint.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> +#include <utils/Log.h> + +#include "hci_hal.h" +#include "osi.h" +#include "semaphore.h" +#include "test_stubs.h" +#include "vendor.h" +} + +DECLARE_TEST_MODES( + init, + open, + close_fn, + transmit, + read_synchronous, + read_async_reentry, + type_byte_only +); + +static char sample_data1[100] = "A point is that which has no part."; +static char sample_data2[100] = "A line is breadthless length."; +static char sample_data3[100] = "The ends of a line are points."; +static char acl_data[100] = "A straight line is a line which lies evenly with the points on itself."; +static char sco_data[100] = "A surface is that which has length and breadth only."; +static char event_data[100] = "The edges of a surface are lines."; + +static const hci_hal_interface_t *hal; +static int dummy_serial_fd; +static int send_command_call_number; +static int reentry_i = 0; + +static semaphore_t *done; +static semaphore_t *reentry_semaphore; + +static void expect_packet_synchronous(serial_data_type_t type, char *packet_data) { + int length = strlen(packet_data); + for (int i = 0; i < length; i++) { + EXPECT_EQ(packet_data[i], hal->read_byte(type)); + } + + hal->packet_finished(type); +} + +STUB_FUNCTION(int, vendor_send_command, (vendor_opcode_t opcode, void *param)) + DURING(open) AT_CALL(0) { + EXPECT_EQ(VENDOR_OPEN_USERIAL, opcode); + // Give back the dummy fd and the number 1 to say we opened 1 port + ((int *)param)[0] = dummy_serial_fd; + return 1; + } + + DURING(close_fn) AT_CALL(0) { + EXPECT_EQ(VENDOR_CLOSE_USERIAL, opcode); + return 0; + } + + UNEXPECTED_CALL; + return 0; +} + +STUB_FUNCTION(void, data_ready_callback, (serial_data_type_t type)) + DURING(read_synchronous) { + AT_CALL(0) { + EXPECT_EQ(DATA_TYPE_ACL, type); + expect_packet_synchronous(type, acl_data); + return; + } + AT_CALL(1) { + EXPECT_EQ(DATA_TYPE_SCO, type); + expect_packet_synchronous(type, sco_data); + return; + } + AT_CALL(2) { + EXPECT_EQ(DATA_TYPE_EVENT, type); + expect_packet_synchronous(type, event_data); + semaphore_post(done); + return; + } + } + + DURING(read_async_reentry) { + EXPECT_EQ(DATA_TYPE_ACL, type); + + while (hal->has_byte(type)) { + EXPECT_EQ(sample_data3[reentry_i], hal->read_byte(type)); + semaphore_post(reentry_semaphore); + reentry_i++; + if (reentry_i == (int)strlen(sample_data3)) { + hal->packet_finished(type); + return; + } + } + + return; + } + + UNEXPECTED_CALL; +} + +void reset_for(TEST_MODES_T next) { + RESET_CALL_COUNT(vendor_send_command); + RESET_CALL_COUNT(data_ready_callback); + CURRENT_TEST_MODE = next; +} + +class HciHalH4Test : public ::testing::Test { + protected: + virtual void SetUp() { + hal = hci_hal_get_test_interface(&vendor); + vendor.send_command = vendor_send_command; + callbacks.data_ready = data_ready_callback; + + send_command_call_number = 0; + + socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd); + dummy_serial_fd = sockfd[0]; + done = semaphore_new(0); + thread = thread_new("hal_test"); + + reset_for(init); + EXPECT_TRUE(hal->init(&callbacks, thread)); + + reset_for(open); + EXPECT_TRUE(hal->open()); + EXPECT_CALL_COUNT(vendor_send_command, 1); + } + + virtual void TearDown() { + reset_for(close_fn); + hal->close(); + EXPECT_CALL_COUNT(vendor_send_command, 1); + + semaphore_free(done); + thread_free(thread); + } + + int sockfd[2]; + vendor_interface_t vendor; + thread_t *thread; + hci_hal_callbacks_t callbacks; +}; + +static void expect_socket_data(int fd, char first_byte, char *data) { + int length = strlen(data) + 1; // + 1 for data type code + int i; + + for (i = 0; i < length; i++) { + fd_set read_fds; + FD_ZERO(&read_fds); + FD_SET(fd, &read_fds); + select(fd + 1, &read_fds, NULL, NULL, NULL); + + char byte; + read(fd, &byte, 1); + + EXPECT_EQ(i == 0 ? first_byte : data[i - 1], byte); + } +} + +static void write_packet(int fd, char first_byte, char *data) { + write(fd, &first_byte, 1); + write(fd, data, strlen(data)); +} + +static void write_packet_reentry(int fd, char first_byte, char *data) { + write(fd, &first_byte, 1); + + int length = strlen(data); + for (int i = 0; i < length; i++) { + write(fd, &data[i], 1); + semaphore_wait(reentry_semaphore); + } +} + +TEST_F(HciHalH4Test, test_transmit) { + reset_for(transmit); + + // Send a command packet + hal->transmit_data(DATA_TYPE_COMMAND, (uint8_t *)(sample_data1 + 1), strlen(sample_data1 + 1)); + expect_socket_data(sockfd[1], DATA_TYPE_COMMAND, sample_data1 + 1); + + // Send an acl packet + hal->transmit_data(DATA_TYPE_ACL, (uint8_t *)(sample_data2 + 1), strlen(sample_data2 + 1)); + expect_socket_data(sockfd[1], DATA_TYPE_ACL, sample_data2 + 1); + + // Send an sco packet + hal->transmit_data(DATA_TYPE_SCO, (uint8_t *)(sample_data3 + 1), strlen(sample_data3 + 1)); + expect_socket_data(sockfd[1], DATA_TYPE_SCO, sample_data3 + 1); +} + +TEST_F(HciHalH4Test, test_read_synchronous) { + reset_for(read_synchronous); + + write_packet(sockfd[1], DATA_TYPE_ACL, acl_data); + write_packet(sockfd[1], DATA_TYPE_SCO, sco_data); + write_packet(sockfd[1], DATA_TYPE_EVENT, event_data); + + // Wait for all data to be received before calling the test good + semaphore_wait(done); + EXPECT_CALL_COUNT(data_ready_callback, 3); +} + +TEST_F(HciHalH4Test, test_read_async_reentry) { + reset_for(read_async_reentry); + + reentry_semaphore = semaphore_new(0); + reentry_i = 0; + + write_packet_reentry(sockfd[1], DATA_TYPE_ACL, sample_data3); + + // write_packet_reentry ensures the data has been received + semaphore_free(reentry_semaphore); +} + +TEST_F(HciHalH4Test, test_type_byte_only_must_not_signal_data_ready) { + reset_for(type_byte_only); + + char byte = DATA_TYPE_ACL; + write(sockfd[1], &byte, 1); + + fd_set read_fds; + + // Wait until the byte we wrote was picked up + do { + FD_ZERO(&read_fds); + FD_SET(sockfd[0], &read_fds); + + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + select(sockfd[0] + 1, &read_fds, NULL, NULL, &timeout); + } while(FD_ISSET(sockfd[0], &read_fds)); +} diff --git a/hci/test/hci_layer_test.cpp b/hci/test/hci_layer_test.cpp new file mode 100644 index 000000000..2ac9012d2 --- /dev/null +++ b/hci/test/hci_layer_test.cpp @@ -0,0 +1,584 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * 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. + * + ******************************************************************************/ + +#include <gtest/gtest.h> + +extern "C" { +#include <stdint.h> +#include <utils/Log.h> + +#include "AlarmTestHarness.h" +#include "btsnoop.h" +#include "hci_hal.h" +#include "hci_inject.h" +#include "hci_layer.h" +#include "low_power_manager.h" +#include "osi.h" +#include "packet_fragmenter.h" +#include "semaphore.h" +#include "test_stubs.h" +#include "vendor.h" +} + +DECLARE_TEST_MODES( + init, + cleanup, + cycle_power, + preload, + postload, + transmit_simple, + receive_simple, + send_internal_command, + turn_on_logging, + turn_off_logging +); + +static const char *small_sample_data = "\"It is easy to see,\" replied Don Quixote"; +static const char *command_sample_data = "that thou art not used to this business of adventures; those are giants"; + +static const char *logging_path = "this/is/a/test/logging/path"; + +static const hci_interface_t *hci; +static bdaddr_t test_addr = (bdaddr_t)"testaddress123"; +static const hci_hal_callbacks_t *hal_callbacks; +static thread_t *internal_thread; +static vendor_cb firmware_config_callback; +static vendor_cb sco_config_callback; +static vendor_cb epilog_callback; +static send_internal_command_cb send_internal_command_callback; +static semaphore_t *done; +static const uint16_t test_handle = (0x1992 & 0xCFFF); +static const uint16_t test_handle_continuation = (0x1992 & 0xCFFF) | 0x1000; +static int packet_index; +static unsigned int data_size_sum; +static BT_HDR *data_to_receive; + +// TODO move this to a common packet testing helper +static BT_HDR *manufacture_packet(uint16_t event, const char *data) { + uint16_t data_length = strlen(data); + uint16_t size = data_length; + if (event == MSG_STACK_TO_HC_HCI_ACL) { + size += 4; // 2 for the handle, 2 for the length; + } + + BT_HDR *packet = (BT_HDR *)malloc(size + sizeof(BT_HDR)); + packet->len = size; + packet->offset = 0; + packet->event = event; + packet->layer_specific = 0; + uint8_t *packet_data = packet->data; + + if (event == MSG_STACK_TO_HC_HCI_ACL) { + UINT16_TO_STREAM(packet_data, test_handle); + UINT16_TO_STREAM(packet_data, data_length); + } + + for (int i = 0; i < data_length; i++) { + packet_data[i] = data[i]; + } + + return packet; +} + +static void expect_packet(uint16_t event, int max_acl_data_size, const uint8_t *data, uint16_t data_length, const char *expected_data) { + int expected_data_offset; + int length_to_check; + + if (event == MSG_STACK_TO_HC_HCI_ACL) { + uint16_t handle; + uint16_t length; + STREAM_TO_UINT16(handle, data); + STREAM_TO_UINT16(length, data); + + if (packet_index == 0) + EXPECT_EQ(test_handle, handle); + else + EXPECT_EQ(test_handle_continuation, handle); + + int length_remaining = strlen(expected_data) - data_size_sum; + int packet_data_length = data_length - HCI_ACL_PREAMBLE_SIZE; + EXPECT_EQ(length_remaining, length); + + if (length_remaining < max_acl_data_size) + EXPECT_EQ(length, packet_data_length); + else + EXPECT_EQ(max_acl_data_size, packet_data_length); + + length_to_check = packet_data_length; + expected_data_offset = packet_index * max_acl_data_size; + packet_index++; + } else { + length_to_check = strlen(expected_data); + expected_data_offset = 0; + } + + for (int i = 0; i < length_to_check; i++) { + EXPECT_EQ(expected_data[expected_data_offset + i], data[i]); + data_size_sum++; + } +} + +STUB_FUNCTION(bool, hal_init, (const hci_hal_callbacks_t *callbacks, thread_t *working_thread)) + DURING(init) AT_CALL(0) { + hal_callbacks = callbacks; + internal_thread = working_thread; + return true; + } + + UNEXPECTED_CALL; + return false; +} + +STUB_FUNCTION(bool, hal_open, ()) + DURING(preload) AT_CALL(0) return true; + UNEXPECTED_CALL; + return false; +} + +STUB_FUNCTION(void, hal_close, ()) + DURING(cleanup) AT_CALL(0) return; + UNEXPECTED_CALL; +} + +STUB_FUNCTION(uint16_t, hal_transmit_data, (serial_data_type_t type, uint8_t *data, uint16_t length)) + DURING(transmit_simple) AT_CALL(0) { + EXPECT_EQ(DATA_TYPE_ACL, type); + expect_packet(MSG_STACK_TO_HC_HCI_ACL, 1021, data, length, small_sample_data); + return length; + } + + DURING(send_internal_command) AT_CALL(0) { + EXPECT_EQ(DATA_TYPE_COMMAND, type); + expect_packet(MSG_STACK_TO_HC_HCI_CMD, 1021, data, length, command_sample_data); + return length; + } + + UNEXPECTED_CALL; + return 0; +} + +STUB_FUNCTION(uint8_t, hal_read_byte, (serial_data_type_t type)) + DURING(receive_simple) { + EXPECT_EQ(DATA_TYPE_ACL, type); + if (data_to_receive->offset < data_to_receive->len) { + return data_to_receive->data[data_to_receive->offset++]; + } + } + + UNEXPECTED_CALL; + return 0; +} + +STUB_FUNCTION(bool, hal_has_byte, (serial_data_type_t type)) + DURING(receive_simple) { + EXPECT_EQ(DATA_TYPE_ACL, type); + return data_to_receive->offset < data_to_receive->len; + } + + UNEXPECTED_CALL; + return false; +} + +STUB_FUNCTION(void, hal_packet_finished, (serial_data_type_t type)) + DURING(receive_simple) AT_CALL(0) { + EXPECT_EQ(DATA_TYPE_ACL, type); + return; + } + + UNEXPECTED_CALL; +} + +STUB_FUNCTION(void, btsnoop_open, (const char *path)) + DURING(turn_on_logging) AT_CALL(0) { + EXPECT_EQ(logging_path, path); + return; + } + + UNEXPECTED_CALL; +} + +STUB_FUNCTION(void, btsnoop_close, ()) + DURING(turn_off_logging) AT_CALL(0) return; + UNEXPECTED_CALL; +} + +STUB_FUNCTION(bool, hci_inject_open, ( + UNUSED_ATTR const hci_interface_t *hci_interface, + UNUSED_ATTR const allocator_t *buffer_allocator)) + DURING(init) AT_CALL(0) return true; + UNEXPECTED_CALL; + return false; +} + +STUB_FUNCTION(void, hci_inject_close, ()) + DURING(cleanup) AT_CALL(0) return; + UNEXPECTED_CALL; +} + +STUB_FUNCTION(void, btsnoop_capture, (const BT_HDR *buffer, bool is_received)) + DURING(transmit_simple) AT_CALL(0) { + EXPECT_FALSE(is_received); + expect_packet(MSG_STACK_TO_HC_HCI_ACL, 1021, buffer->data + buffer->offset, buffer->len, small_sample_data); + packet_index = 0; + data_size_sum = 0; + return; + } + + DURING(send_internal_command) AT_CALL(0) { + EXPECT_FALSE(is_received); + expect_packet(MSG_STACK_TO_HC_HCI_CMD, 1021, buffer->data + buffer->offset, buffer->len, command_sample_data); + packet_index = 0; + data_size_sum = 0; + return; + } + + DURING(receive_simple) AT_CALL(0) { + EXPECT_TRUE(is_received); + EXPECT_TRUE(buffer->len == data_to_receive->len); + const uint8_t *buffer_base = buffer->data + buffer->offset; + const uint8_t *expected_base = data_to_receive->data; + for (int i = 0; i < buffer->len; i++) { + EXPECT_EQ(expected_base[i], buffer_base[i]); + } + + return; + } + + UNEXPECTED_CALL; +} + +STUB_FUNCTION(void, low_power_init, (UNUSED_ATTR thread_t *thread)) + DURING(init) AT_CALL(0) return; + UNEXPECTED_CALL; +} + +STUB_FUNCTION(void, low_power_cleanup, ()) + DURING(cleanup) AT_CALL(0) return; + UNEXPECTED_CALL; +} + +STUB_FUNCTION(void, low_power_wake_assert, ()) + DURING(transmit_simple) AT_CALL(0) return; + DURING(send_internal_command) AT_CALL(0) return; + UNEXPECTED_CALL; +} + +STUB_FUNCTION(void, low_power_transmit_done, ()) + DURING(transmit_simple) AT_CALL(0) return; + DURING(send_internal_command) AT_CALL(0) return; + UNEXPECTED_CALL; +} + +STUB_FUNCTION(bool, vendor_open, (const uint8_t *addr, const allocator_t *allocator)) + DURING(init) AT_CALL(0) { + EXPECT_EQ(test_addr, addr); + EXPECT_EQ(&allocator_malloc, allocator); + return true; + } + + UNEXPECTED_CALL; + return true; +} + +STUB_FUNCTION(void, vendor_close, ()) + DURING(cleanup) AT_CALL(0) return; + UNEXPECTED_CALL; +} + +STUB_FUNCTION(void, vendor_set_callback, (vendor_async_opcode_t opcode, UNUSED_ATTR vendor_cb callback)) + DURING(init) { + AT_CALL(0) { + EXPECT_EQ(VENDOR_CONFIGURE_FIRMWARE, opcode); + firmware_config_callback = callback; + return; + } + AT_CALL(1) { + EXPECT_EQ(VENDOR_CONFIGURE_SCO, opcode); + sco_config_callback = callback; + return; + } + AT_CALL(2) { + EXPECT_EQ(VENDOR_DO_EPILOG, opcode); + epilog_callback = callback; + return; + } + } + + UNEXPECTED_CALL; +} + +STUB_FUNCTION(void, vendor_set_send_internal_command_callback, (UNUSED_ATTR send_internal_command_cb callback)) + DURING(init) AT_CALL (0) { + EXPECT_TRUE(callback != NULL); + send_internal_command_callback = callback; + return; + } + + UNEXPECTED_CALL; +} + +STUB_FUNCTION(int, vendor_send_command, (vendor_opcode_t opcode, void *param)) + DURING(cleanup) AT_CALL(0) { + EXPECT_EQ(VENDOR_CHIP_POWER_CONTROL, opcode); + EXPECT_EQ(BT_VND_PWR_OFF, *(int *)param); + return 0; + } + + DURING(cycle_power) { + AT_CALL(0) { + EXPECT_EQ(VENDOR_CHIP_POWER_CONTROL, opcode); + EXPECT_EQ(BT_VND_PWR_ON, *(int *)param); + return 0; + } + AT_CALL(1) { + EXPECT_EQ(VENDOR_CHIP_POWER_CONTROL, opcode); + EXPECT_EQ(BT_VND_PWR_OFF, *(int *)param); + return 0; + } + } + + UNEXPECTED_CALL; + return 0; +} + +STUB_FUNCTION(int, vendor_send_async_command, (UNUSED_ATTR vendor_async_opcode_t opcode, UNUSED_ATTR void *param)) + DURING(preload) AT_CALL(0) { + EXPECT_EQ(VENDOR_CONFIGURE_FIRMWARE, opcode); + return 0; + } + + DURING(postload) AT_CALL(0) { + EXPECT_EQ(VENDOR_CONFIGURE_SCO, opcode); + return 0; + } + + UNEXPECTED_CALL; + return 0; +} + +STUB_FUNCTION(void, callback_transmit_finished, (UNUSED_ATTR void *buffer, bool all_fragments_sent)) + DURING(transmit_simple) AT_CALL(0) { + EXPECT_TRUE(all_fragments_sent); + return; + } + + DURING(send_internal_command) AT_CALL(0) { + EXPECT_TRUE(all_fragments_sent); + return; + } + + UNEXPECTED_CALL; +} + +STUB_FUNCTION(void, internal_command_response, (UNUSED_ATTR void *response)) + UNEXPECTED_CALL; +} + +static void reset_for(TEST_MODES_T next) { + RESET_CALL_COUNT(vendor_open); + RESET_CALL_COUNT(vendor_close); + RESET_CALL_COUNT(vendor_set_callback); + RESET_CALL_COUNT(vendor_set_send_internal_command_callback); + RESET_CALL_COUNT(vendor_send_command); + RESET_CALL_COUNT(vendor_send_async_command); + RESET_CALL_COUNT(hal_init); + RESET_CALL_COUNT(hal_open); + RESET_CALL_COUNT(hal_close); + RESET_CALL_COUNT(hal_read_byte); + RESET_CALL_COUNT(hal_has_byte); + RESET_CALL_COUNT(hal_packet_finished); + RESET_CALL_COUNT(hal_transmit_data); + RESET_CALL_COUNT(btsnoop_open); + RESET_CALL_COUNT(btsnoop_close); + RESET_CALL_COUNT(btsnoop_capture); + RESET_CALL_COUNT(hci_inject_open); + RESET_CALL_COUNT(hci_inject_close); + RESET_CALL_COUNT(low_power_init); + RESET_CALL_COUNT(low_power_cleanup); + RESET_CALL_COUNT(low_power_wake_assert); + RESET_CALL_COUNT(low_power_transmit_done); + RESET_CALL_COUNT(callback_transmit_finished); + RESET_CALL_COUNT(internal_command_response); + CURRENT_TEST_MODE = next; +} + +class HciLayerTest : public AlarmTestHarness { + protected: + virtual void SetUp() { + AlarmTestHarness::SetUp(); + + hci = hci_layer_get_test_interface( + &hal, + &btsnoop, + &hci_inject, + packet_fragmenter_get_interface(), + &vendor, + &low_power_manager + ); + + packet_index = 0; + data_size_sum = 0; + + vendor.open = vendor_open; + vendor.close = vendor_close; + vendor.set_callback = vendor_set_callback; + vendor.set_send_internal_command_callback = vendor_set_send_internal_command_callback; + vendor.send_command = vendor_send_command; + vendor.send_async_command = vendor_send_async_command; + hal.init = hal_init; + hal.open = hal_open; + hal.close = hal_close; + hal.read_byte = hal_read_byte; + hal.has_byte = hal_has_byte; + hal.packet_finished = hal_packet_finished; + hal.transmit_data = hal_transmit_data; + btsnoop.open = btsnoop_open; + btsnoop.close = btsnoop_close; + btsnoop.capture = btsnoop_capture; + hci_inject.open = hci_inject_open; + hci_inject.close = hci_inject_close; + low_power_manager.init = low_power_init; + low_power_manager.cleanup = low_power_cleanup; + low_power_manager.wake_assert = low_power_wake_assert; + low_power_manager.transmit_done = low_power_transmit_done; + callbacks.transmit_finished = callback_transmit_finished; + + done = semaphore_new(0); + + reset_for(init); + hci->init(test_addr, &allocator_malloc, &callbacks); + + EXPECT_CALL_COUNT(vendor_open, 1); + EXPECT_CALL_COUNT(hal_init, 1); + EXPECT_CALL_COUNT(low_power_init, 1); + EXPECT_CALL_COUNT(vendor_set_callback, 3); + EXPECT_CALL_COUNT(vendor_set_send_internal_command_callback, 1); + } + + virtual void TearDown() { + reset_for(cleanup); + hci->cleanup(); + + EXPECT_CALL_COUNT(low_power_cleanup, 1); + EXPECT_CALL_COUNT(hal_close, 1); + EXPECT_CALL_COUNT(vendor_send_command, 1); + EXPECT_CALL_COUNT(vendor_close, 1); + + semaphore_free(done); + AlarmTestHarness::TearDown(); + } + + hci_hal_interface_t hal; + btsnoop_interface_t btsnoop; + hci_inject_interface_t hci_inject; + vendor_interface_t vendor; + low_power_manager_interface_t low_power_manager; + hci_callbacks_t callbacks; +}; + +static void signal_work_item(UNUSED_ATTR void *context) { + semaphore_post(done); +} + +static void flush_thread(thread_t *thread) { + thread_post(thread, signal_work_item, NULL); + semaphore_wait(done); +} + +TEST_F(HciLayerTest, test_cycle_power) { + reset_for(cycle_power); + hci->set_chip_power_on(true); + hci->set_chip_power_on(false); + + EXPECT_CALL_COUNT(vendor_send_command, 2); +} + +TEST_F(HciLayerTest, test_preload) { + reset_for(preload); + hci->do_preload(); + + flush_thread(internal_thread); + EXPECT_CALL_COUNT(hal_open, 1); + EXPECT_CALL_COUNT(vendor_send_async_command, 1); +} + +TEST_F(HciLayerTest, test_postload) { + reset_for(postload); + hci->do_postload(); + + flush_thread(internal_thread); + EXPECT_CALL_COUNT(vendor_send_async_command, 1); +} + +TEST_F(HciLayerTest, test_transmit_simple) { + reset_for(transmit_simple); + BT_HDR *packet = manufacture_packet(MSG_STACK_TO_HC_HCI_ACL, small_sample_data); + hci->transmit_downward(MSG_STACK_TO_HC_HCI_ACL, packet); + + flush_thread(internal_thread); + EXPECT_CALL_COUNT(hal_transmit_data, 1); + EXPECT_CALL_COUNT(btsnoop_capture, 1); + EXPECT_CALL_COUNT(low_power_transmit_done, 1); + EXPECT_CALL_COUNT(low_power_wake_assert, 1); + EXPECT_CALL_COUNT(callback_transmit_finished, 1); + + free(packet); +} + +// TODO finish test +TEST_F(HciLayerTest, test_receive_simple) { + reset_for(receive_simple); + data_to_receive = manufacture_packet(MSG_STACK_TO_HC_HCI_ACL, small_sample_data); + + // Not running on the internal thread, unlike the real hal + hal_callbacks->data_ready(DATA_TYPE_ACL); + EXPECT_CALL_COUNT(hal_packet_finished, 1); + EXPECT_CALL_COUNT(btsnoop_capture, 1); + + free(data_to_receive); +} + +TEST_F(HciLayerTest, test_send_internal_command) { + // Send a test command + reset_for(send_internal_command); + data_to_receive = manufacture_packet(MSG_STACK_TO_HC_HCI_CMD, command_sample_data); + send_internal_command_callback(MSG_STACK_TO_HC_HCI_CMD, data_to_receive, internal_command_response); + + flush_thread(internal_thread); + EXPECT_CALL_COUNT(hal_transmit_data, 1); + EXPECT_CALL_COUNT(btsnoop_capture, 1); + EXPECT_CALL_COUNT(low_power_transmit_done, 1); + EXPECT_CALL_COUNT(low_power_wake_assert, 1); + EXPECT_CALL_COUNT(callback_transmit_finished, 1); + + // TODO send a response and make sure the response ends up at the callback + + free(data_to_receive); +} + +TEST_F(HciLayerTest, test_turn_on_logging) { + reset_for(turn_on_logging); + hci->turn_on_logging(logging_path); + EXPECT_CALL_COUNT(btsnoop_open, 1); +} + +TEST_F(HciLayerTest, test_turn_off_logging) { + reset_for(turn_off_logging); + hci->turn_off_logging(); + EXPECT_CALL_COUNT(btsnoop_close, 1); +} diff --git a/hci/test/low_power_manager_test.cpp b/hci/test/low_power_manager_test.cpp new file mode 100644 index 000000000..c108038eb --- /dev/null +++ b/hci/test/low_power_manager_test.cpp @@ -0,0 +1,140 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * 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. + * + ******************************************************************************/ + +#include <gtest/gtest.h> + +extern "C" { +#include <stdint.h> +#include <utils/Log.h> + +#include "AlarmTestHarness.h" +#include "low_power_manager.h" +#include "osi.h" +#include "semaphore.h" +#include "test_stubs.h" +#include "thread.h" +#include "vendor.h" +} + +DECLARE_TEST_MODES( + init, + cleanup, + enable_disable +); + +static const low_power_manager_interface_t *manager; +static thread_t *thread; +static semaphore_t *done; + +static vendor_cb low_power_state_callback; + +static void flush_work_queue_item(UNUSED_ATTR void *context) { + semaphore_post(done); +} + +STUB_FUNCTION(int, vendor_send_command, (vendor_opcode_t opcode, void *param)) + DURING(init) AT_CALL(0) { + EXPECT_EQ(VENDOR_GET_LPM_IDLE_TIMEOUT, opcode); + *((uint32_t *)param) = 100; + return 0; + } + + UNEXPECTED_CALL; + return 0; +} + +STUB_FUNCTION(int, vendor_send_async_command, (vendor_async_opcode_t opcode, void *param)) + DURING(enable_disable) { + AT_CALL(0) { + EXPECT_EQ(VENDOR_SET_LPM_MODE, opcode); + EXPECT_EQ(BT_VND_LPM_ENABLE, *(uint8_t *)param); + low_power_state_callback(true); + thread_post(thread, flush_work_queue_item, NULL); + return 0; + } + AT_CALL(1) { + EXPECT_EQ(VENDOR_SET_LPM_MODE, opcode); + EXPECT_EQ(BT_VND_LPM_DISABLE, *(uint8_t *)param); + low_power_state_callback(true); + thread_post(thread, flush_work_queue_item, NULL); + return 0; + } + } + + UNEXPECTED_CALL; + return 0; +} + +STUB_FUNCTION(void, vendor_set_callback, (vendor_async_opcode_t opcode, vendor_cb callback)) + DURING(init) AT_CALL(0) { + EXPECT_EQ(VENDOR_SET_LPM_MODE, opcode); + low_power_state_callback = callback; + return; + } + + UNEXPECTED_CALL; +} + +static void reset_for(TEST_MODES_T next) { + RESET_CALL_COUNT(vendor_send_command); + RESET_CALL_COUNT(vendor_send_async_command); + RESET_CALL_COUNT(vendor_set_callback); + CURRENT_TEST_MODE = next; +} + +class LowPowerManagerTest : public AlarmTestHarness { + protected: + virtual void SetUp() { + AlarmTestHarness::SetUp(); + low_power_state_callback = NULL; + vendor.send_command = vendor_send_command; + vendor.send_async_command = vendor_send_async_command; + vendor.set_callback = vendor_set_callback; + manager = low_power_manager_get_test_interface(&vendor); + thread = thread_new("test_thread"); + done = semaphore_new(0); + + reset_for(init); + manager->init(thread); + + EXPECT_CALL_COUNT(vendor_send_command, 1); + EXPECT_CALL_COUNT(vendor_set_callback, 1); + } + + virtual void TearDown() { + reset_for(cleanup); + manager->cleanup(); + + semaphore_free(done); + thread_free(thread); + AlarmTestHarness::TearDown(); + } + + vendor_interface_t vendor; +}; + +TEST_F(LowPowerManagerTest, test_enable_disable) { + reset_for(enable_disable); + manager->post_command(LPM_ENABLE); + semaphore_wait(done); + + manager->post_command(LPM_DISABLE); + semaphore_wait(done); + + EXPECT_CALL_COUNT(vendor_send_async_command, 2); +} diff --git a/hci/test/packet_fragmenter_test.cpp b/hci/test/packet_fragmenter_test.cpp new file mode 100644 index 000000000..1a9f4f371 --- /dev/null +++ b/hci/test/packet_fragmenter_test.cpp @@ -0,0 +1,367 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * 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. + * + ******************************************************************************/ + +#include <gtest/gtest.h> + +extern "C" { +#include <stdint.h> +#include <utils/Log.h> + +#include "hci_internals.h" +#include "packet_fragmenter.h" +#include "osi.h" +#include "test_stubs.h" +} + +DECLARE_TEST_MODES( + init, + set_data_sizes, + no_fragmentation, + fragmentation, + ble_no_fragmentation, + ble_fragmentation, + non_acl_passthrough_fragmentation, + no_reassembly, + reassembly, + non_acl_passthrough_reassembly +); + +#define LOCAL_BLE_CONTROLLER_ID 1 + +static const char *sample_data = + "At this point they came in sight of thirty forty windmills that there are on plain, and " + "as soon as Don Quixote saw them he said to his squire, \"Fortune is arranging matters " + "for us better than we could have shaped our desires ourselves, for look there, friend " + "Sancho Panza, where thirty or more monstrous giants present themselves, all of whom I " + "mean to engage in battle and slay, and with whose spoils we shall begin to make our " + "fortunes; for this is righteous warfare, and it is God's good service to sweep so evil " + "a breed from off the face of the earth.\""; + +static const char *small_sample_data = "\"What giants?\" said Sancho Panza."; +static const uint16_t test_handle_start = (0x1992 & 0xCFFF) | 0x2000; +static const uint16_t test_handle_continuation = (0x1992 & 0xCFFF) | 0x1000; +static int packet_index; +static unsigned int data_size_sum; + +static const packet_fragmenter_interface_t *fragmenter; + +static BT_HDR *manufacture_packet_for_fragmentation(uint16_t event, const char *data) { + uint16_t data_length = strlen(data); + uint16_t size = data_length; + if (event == MSG_STACK_TO_HC_HCI_ACL) { + size += 4; // 2 for the handle, 2 for the length; + } + + BT_HDR *packet = (BT_HDR *)malloc(size + sizeof(BT_HDR)); + packet->len = size; + packet->offset = 0; + packet->event = event; + packet->layer_specific = 0; + uint8_t *packet_data = packet->data; + + if (event == MSG_STACK_TO_HC_HCI_ACL) { + UINT16_TO_STREAM(packet_data, test_handle_start); + UINT16_TO_STREAM(packet_data, data_length); + } + + memcpy(packet_data, data, data_length); + return packet; +} + +static void expect_packet_fragmented(uint16_t event, int max_acl_data_size, BT_HDR *packet, const char *expected_data, bool send_complete) { + uint8_t *data = packet->data + packet->offset; + int expected_data_offset; + int length_to_check; + + if (event == MSG_STACK_TO_HC_HCI_ACL) { + uint16_t handle; + uint16_t length; + STREAM_TO_UINT16(handle, data); + STREAM_TO_UINT16(length, data); + + if (packet_index == 0) + EXPECT_EQ(test_handle_start, handle); + else + EXPECT_EQ(test_handle_continuation, handle); + + int length_remaining = strlen(expected_data) - data_size_sum; + int packet_data_length = packet->len - HCI_ACL_PREAMBLE_SIZE; + EXPECT_EQ(packet_data_length, length); + + if (length_remaining > max_acl_data_size) + EXPECT_EQ(max_acl_data_size, packet_data_length); + + length_to_check = packet_data_length; + expected_data_offset = packet_index * max_acl_data_size; + packet_index++; + } else { + length_to_check = strlen(expected_data); + expected_data_offset = 0; + } + + for (int i = 0; i < length_to_check; i++) { + EXPECT_EQ(expected_data[expected_data_offset + i], data[i]); + data_size_sum++; + } + + if (event == MSG_STACK_TO_HC_HCI_ACL) + EXPECT_TRUE(send_complete == (data_size_sum == strlen(expected_data))); +} + +static void manufacture_packet_and_then_reassemble(uint16_t event, uint16_t acl_size, const char *data) { + uint16_t data_length = strlen(data); + + if (event == MSG_HC_TO_STACK_HCI_ACL) { + uint16_t total_length = data_length + 2; // 2 for l2cap length; + uint16_t length_sent = 0; + uint16_t l2cap_length = data_length - 2; // l2cap length field, 2 for the pretend channel id borrowed from the data + + do { + int length_to_send = (length_sent + (acl_size - 4) < total_length) ? (acl_size - 4) : (total_length - length_sent); + BT_HDR *packet = (BT_HDR *)malloc(length_to_send + 4 + sizeof(BT_HDR)); + packet->len = length_to_send + 4; + packet->offset = 0; + packet->event = event; + packet->layer_specific = 0; + + uint8_t *packet_data = packet->data; + if (length_sent == 0) { // first packet + UINT16_TO_STREAM(packet_data, test_handle_start); + UINT16_TO_STREAM(packet_data, length_to_send); + UINT16_TO_STREAM(packet_data, l2cap_length); + memcpy(packet_data, data, length_to_send - 2); + } else { + UINT16_TO_STREAM(packet_data, test_handle_continuation); + UINT16_TO_STREAM(packet_data, length_to_send); + memcpy(packet_data, data + length_sent - 2, length_to_send); + } + + length_sent += length_to_send; + fragmenter->reassemble_and_dispatch(packet); + } while (length_sent < total_length); + } else { + BT_HDR *packet = (BT_HDR *)malloc(data_length + sizeof(BT_HDR)); + packet->len = data_length; + packet->offset = 0; + packet->event = event; + packet->layer_specific = 0; + memcpy(packet->data, data, data_length); + + fragmenter->reassemble_and_dispatch(packet); + } +} + +static void expect_packet_reassembled(uint16_t event, BT_HDR *packet, const char *expected_data) { + uint16_t expected_data_length = strlen(expected_data); + uint8_t *data = packet->data + packet->offset; + + if (event == MSG_HC_TO_STACK_HCI_ACL) { + uint16_t handle; + uint16_t length; + uint16_t l2cap_length; + STREAM_TO_UINT16(handle, data); + STREAM_TO_UINT16(length, data); + STREAM_TO_UINT16(l2cap_length, data); + + EXPECT_EQ(test_handle_start, handle); + EXPECT_EQ(expected_data_length + 2, length); + EXPECT_EQ(expected_data_length - 2, l2cap_length); // -2 for the pretend channel id + } + + + for (int i = 0; i < expected_data_length; i++) { + EXPECT_EQ(expected_data[i], data[i]); + data_size_sum++; + } +} + +STUB_FUNCTION(void, fragmented_callback, (BT_HDR *packet, bool send_complete)) + DURING(no_fragmentation) AT_CALL(0) { + expect_packet_fragmented(MSG_STACK_TO_HC_HCI_ACL, 42, packet, small_sample_data, send_complete); + return; + } + + DURING(fragmentation) { + expect_packet_fragmented(MSG_STACK_TO_HC_HCI_ACL, 10, packet, sample_data, send_complete); + return; + } + + DURING(ble_no_fragmentation) AT_CALL(0) { + expect_packet_fragmented(MSG_STACK_TO_HC_HCI_ACL, 42, packet, small_sample_data, send_complete); + return; + } + + DURING(ble_fragmentation) { + expect_packet_fragmented(MSG_STACK_TO_HC_HCI_ACL, 10, packet, sample_data, send_complete); + return; + } + + DURING(non_acl_passthrough_fragmentation) AT_CALL(0) { + expect_packet_fragmented(MSG_STACK_TO_HC_HCI_CMD, 10, packet, sample_data, send_complete); + return; + } + + UNEXPECTED_CALL; +} + +STUB_FUNCTION(void, reassembled_callback, (BT_HDR *packet)) + DURING(no_reassembly) AT_CALL(0) { + expect_packet_reassembled(MSG_HC_TO_STACK_HCI_ACL, packet, small_sample_data); + return; + } + + DURING(reassembly) AT_CALL(0) { + expect_packet_reassembled(MSG_HC_TO_STACK_HCI_ACL, packet, sample_data); + return; + } + + DURING(non_acl_passthrough_reassembly) AT_CALL(0) { + expect_packet_reassembled(MSG_HC_TO_STACK_HCI_EVT, packet, sample_data); + return; + } + + UNEXPECTED_CALL; +} + +STUB_FUNCTION(void, transmit_finished_callback, (UNUSED_ATTR void *packet, UNUSED_ATTR bool sent_all_fragments)) + UNEXPECTED_CALL; +} + +static void reset_for(TEST_MODES_T next) { + RESET_CALL_COUNT(fragmented_callback); + RESET_CALL_COUNT(reassembled_callback); + RESET_CALL_COUNT(transmit_finished_callback); + CURRENT_TEST_MODE = next; +} + +class PacketFragmenterTest : public ::testing::Test { + protected: + virtual void SetUp() { + fragmenter = packet_fragmenter_get_interface(); + packet_index = 0; + data_size_sum = 0; + + callbacks.fragmented = fragmented_callback; + callbacks.reassembled = reassembled_callback; + callbacks.transmit_finished = transmit_finished_callback; + + reset_for(init); + fragmenter->init(&callbacks, &allocator_malloc); + } + + virtual void TearDown() { + } + + packet_fragmenter_callbacks_t callbacks; +}; + +TEST_F(PacketFragmenterTest, test_set_data_sizes) { + reset_for(set_data_sizes); + fragmenter->set_acl_data_size(1337); + fragmenter->set_ble_acl_data_size(42); +} + +TEST_F(PacketFragmenterTest, test_no_fragment_necessary) { + reset_for(no_fragmentation); + fragmenter->set_acl_data_size(42); + + BT_HDR *packet = manufacture_packet_for_fragmentation(MSG_STACK_TO_HC_HCI_ACL, small_sample_data); + fragmenter->fragment_and_dispatch(packet); + free(packet); + + EXPECT_EQ(strlen(small_sample_data), data_size_sum); + EXPECT_CALL_COUNT(fragmented_callback, 1); +} + +TEST_F(PacketFragmenterTest, test_fragment_necessary) { + reset_for(fragmentation); + fragmenter->set_acl_data_size(10); + + BT_HDR *packet = manufacture_packet_for_fragmentation(MSG_STACK_TO_HC_HCI_ACL, sample_data); + fragmenter->fragment_and_dispatch(packet); + free(packet); + + EXPECT_EQ(strlen(sample_data), data_size_sum); +} + +TEST_F(PacketFragmenterTest, test_ble_no_fragment_necessary) { + reset_for(ble_no_fragmentation); + fragmenter->set_acl_data_size(1); + fragmenter->set_ble_acl_data_size(42); + + BT_HDR *packet = manufacture_packet_for_fragmentation(MSG_STACK_TO_HC_HCI_ACL, small_sample_data); + packet->event |= LOCAL_BLE_CONTROLLER_ID; + fragmenter->fragment_and_dispatch(packet); + free(packet); + + EXPECT_EQ(strlen(small_sample_data), data_size_sum); + EXPECT_CALL_COUNT(fragmented_callback, 1); +} + +TEST_F(PacketFragmenterTest, test_ble_fragment_necessary) { + reset_for(ble_fragmentation); + fragmenter->set_acl_data_size(10000); + fragmenter->set_ble_acl_data_size(10); + + BT_HDR *packet = manufacture_packet_for_fragmentation(MSG_STACK_TO_HC_HCI_ACL, sample_data); + packet->event |= LOCAL_BLE_CONTROLLER_ID; + fragmenter->fragment_and_dispatch(packet); + free(packet); + + EXPECT_EQ(strlen(sample_data), data_size_sum); +} + +TEST_F(PacketFragmenterTest, test_non_acl_passthrough_fragmentation) { + reset_for(non_acl_passthrough_fragmentation); + fragmenter->set_acl_data_size(42); + fragmenter->set_ble_acl_data_size(42); + + BT_HDR *packet = manufacture_packet_for_fragmentation(MSG_STACK_TO_HC_HCI_CMD, sample_data); + fragmenter->fragment_and_dispatch(packet); + free(packet); + + EXPECT_EQ(strlen(sample_data), data_size_sum); + EXPECT_CALL_COUNT(fragmented_callback, 1); +} + +TEST_F(PacketFragmenterTest, test_no_reassembly_necessary) { + reset_for(no_reassembly); + fragmenter->set_acl_data_size(1337); + manufacture_packet_and_then_reassemble(MSG_HC_TO_STACK_HCI_ACL, 1337, small_sample_data); + + EXPECT_EQ(strlen(small_sample_data), data_size_sum); + EXPECT_CALL_COUNT(reassembled_callback, 1); +} + +TEST_F(PacketFragmenterTest, test_reassembly_necessary) { + reset_for(reassembly); + fragmenter->set_acl_data_size(42); + manufacture_packet_and_then_reassemble(MSG_HC_TO_STACK_HCI_ACL, 42, sample_data); + + EXPECT_EQ(strlen(sample_data), data_size_sum); + EXPECT_CALL_COUNT(reassembled_callback, 1); +} + +TEST_F(PacketFragmenterTest, test_non_acl_passthrough_reasseembly) { + reset_for(non_acl_passthrough_reassembly); + fragmenter->set_acl_data_size(42); + manufacture_packet_and_then_reassemble(MSG_HC_TO_STACK_HCI_EVT, 42, sample_data); + + EXPECT_EQ(strlen(sample_data), data_size_sum); + EXPECT_CALL_COUNT(reassembled_callback, 1); +} diff --git a/main/bte_main.c b/main/bte_main.c index 3b8964ff7..296263d9d 100755 --- a/main/bte_main.c +++ b/main/bte_main.c @@ -30,8 +30,7 @@ #include <signal.h> #include <stdlib.h> #include <time.h> -#include <hardware/bluetooth.h> -#include <cutils/properties.h> +#include <utils/Log.h> #include "alarm.h" #include "bd.h" @@ -39,10 +38,12 @@ #include "bt_hci_bdroid.h" #include "bte.h" #include "btu.h" -#include "bt_hci_lib.h" #include "bt_utils.h" +#include "fixed_queue.h" #include "gki.h" +#include "hci_layer.h" #include "osi.h" +#include "thread.h" /******************************************************************************* ** Constants & Macros @@ -88,13 +89,18 @@ char hci_logfile[256] = HCI_LOGGING_FILENAME; /******************************************************************************* ** Static variables *******************************************************************************/ -static bt_hc_interface_t *bt_hc_if=NULL; -static const bt_hc_callbacks_t hc_callbacks; -static BOOLEAN lpm_enabled = FALSE; +static const hci_interface_t *hci; +static const hci_callbacks_t hci_callbacks; +static const allocator_t buffer_allocator; static bt_preload_retry_cb_t preload_retry_cb; // Lock to serialize cleanup requests from upper layer. static pthread_mutex_t cleanup_lock; +// These are temporary so we can run the new HCI code +// with the old upper stack. +static fixed_queue_t *upbound_data; +static thread_t *dispatch_thread; + /******************************************************************************* ** Static functions *******************************************************************************/ @@ -102,6 +108,7 @@ static void bte_hci_enable(void); static void bte_hci_disable(void); static void preload_start_wait_timer(void); static void preload_stop_wait_timer(void); +static void dump_upbound_data_to_btu(fixed_queue_t *queue, void *context); /******************************************************************************* ** Externs @@ -141,8 +148,21 @@ void bte_main_boot_entry(void) /* initialize OS */ GKI_init(); - if (!(bt_hc_if = (bt_hc_interface_t *)bt_hc_get_interface())) - APPL_TRACE_ERROR("!!! Failed to get BtHostControllerInterface !!!"); + hci = hci_layer_get_interface(); + if (!hci) + ALOGE("%s could not get hci layer interface.", __func__); + + upbound_data = fixed_queue_new(SIZE_MAX); + dispatch_thread = thread_new("hci_dispatch"); + + fixed_queue_register_dequeue( + upbound_data, + thread_get_reactor(dispatch_thread), + dump_upbound_data_to_btu, + NULL + ); + + data_dispatcher_register_default(hci->upward_dispatcher, upbound_data); memset(&preload_retry_cb, 0, sizeof(bt_preload_retry_cb_t)); preload_retry_cb.alarm = alarm_new(); @@ -197,8 +217,6 @@ void bte_main_enable() /* Initialize BTE control block */ BTE_Init(); - lpm_enabled = FALSE; - GKI_create_task((TASKPTR)btu_task, BTU_TASK, BTE_BTU_TASK_STR, (UINT16 *) ((UINT8 *)bte_btu_stack + BTE_BTU_STACK_SIZE), sizeof(bte_btu_stack)); @@ -241,19 +259,18 @@ void bte_main_config_hci_logging(BOOLEAN enable, BOOLEAN bt_disabled) int old = (hci_logging_enabled == TRUE) || (hci_logging_config == TRUE); int new; - if (enable) { - hci_logging_config = TRUE; - } else { - hci_logging_config = FALSE; - } + hci_logging_config = enable; new = (hci_logging_enabled == TRUE) || (hci_logging_config == TRUE); - if ((old == new) || bt_disabled || (bt_hc_if == NULL)) { + if ((old == new) || bt_disabled) { return; } - bt_hc_if->logging(new ? BT_HC_LOGGING_ON : BT_HC_LOGGING_OFF, hci_logfile, hci_save_log); + if (new) + hci->turn_on_logging(hci_logfile); + else + hci->turn_off_logging(); } /****************************************************************************** @@ -271,38 +288,34 @@ static void bte_hci_enable(void) preload_start_wait_timer(); - if (bt_hc_if) - { - int result = bt_hc_if->init(&hc_callbacks, btif_local_bd_addr.address); - APPL_TRACE_EVENT("libbt-hci init returns %d", result); + bool success = hci->init(btif_local_bd_addr.address, &buffer_allocator, &hci_callbacks); + APPL_TRACE_EVENT("libbt-hci init returns %d", success); - assert(result == BT_HC_STATUS_SUCCESS); + assert(success); - if (hci_logging_enabled == TRUE || hci_logging_config == TRUE) - bt_hc_if->logging(BT_HC_LOGGING_ON, hci_logfile, hci_save_log); + if (hci_logging_enabled == TRUE || hci_logging_config == TRUE) + hci->turn_on_logging(hci_logfile); #if (defined (BT_CLEAN_TURN_ON_DISABLED) && BT_CLEAN_TURN_ON_DISABLED == TRUE) - APPL_TRACE_DEBUG("%s Not Turninig Off the BT before Turninig ON", __FUNCTION__); + APPL_TRACE_DEBUG("%s not turning off the chip before turning it on", __FUNCTION__); - /* Do not power off the chip before powering on if BT_CLEAN_TURN_ON_DISABLED flag - is defined and set to TRUE to avoid below mentioned issue. + /* Do not power off the chip before powering on if BT_CLEAN_TURN_ON_DISABLED flag + is defined and set to TRUE to avoid below mentioned issue. - Wingray kernel driver maintains a combined counter to keep track of - BT-Wifi state. Invoking set_power(BT_HC_CHIP_PWR_OFF) when the BT is already - in OFF state causes this counter to be incorrectly decremented and results in undesired - behavior of the chip. + Wingray kernel driver maintains a combined counter to keep track of + BT-Wifi state. Invoking set_power(BT_HC_CHIP_PWR_OFF) when the BT is already + in OFF state causes this counter to be incorrectly decremented and results in undesired + behavior of the chip. - This is only a workaround and when the issue is fixed in the kernel this work around - should be removed. */ + This is only a workaround and when the issue is fixed in the kernel this work around + should be removed. */ #else - /* toggle chip power to ensure we will reset chip in case - a previous stack shutdown wasn't completed gracefully */ - bt_hc_if->set_power(BT_HC_CHIP_PWR_OFF); + /* toggle chip power to ensure we will reset chip in case + a previous stack shutdown wasn't completed gracefully */ + hci->set_chip_power_on(false); #endif - bt_hc_if->set_power(BT_HC_CHIP_PWR_ON); - - bt_hc_if->preload(NULL); - } + hci->set_chip_power_on(true); + hci->do_preload(); } /****************************************************************************** @@ -318,15 +331,15 @@ static void bte_hci_disable(void) { APPL_TRACE_DEBUG("%s", __FUNCTION__); - if (!bt_hc_if) + if (!hci) return; // Cleanup is not thread safe and must be protected. pthread_mutex_lock(&cleanup_lock); if (hci_logging_enabled == TRUE || hci_logging_config == TRUE) - bt_hc_if->logging(BT_HC_LOGGING_OFF, hci_logfile, hci_save_log); - bt_hc_if->cleanup(); + hci->turn_off_logging(); + hci->cleanup(); pthread_mutex_unlock(&cleanup_lock); } @@ -403,8 +416,7 @@ static void preload_stop_wait_timer(void) ******************************************************************************/ void bte_main_postload_cfg(void) { - if (bt_hc_if) - bt_hc_if->postload(NULL); + hci->do_postload(); } #if (defined(HCILP_INCLUDED) && HCILP_INCLUDED == TRUE) @@ -419,14 +431,7 @@ void bte_main_postload_cfg(void) ******************************************************************************/ void bte_main_enable_lpm(BOOLEAN enable) { - int result = -1; - - if (bt_hc_if) - result = bt_hc_if->lpm( \ - (enable == TRUE) ? BT_HC_LPM_ENABLE : BT_HC_LPM_DISABLE \ - ); - - APPL_TRACE_EVENT("HC lib lpm enable=%d return %d", enable, result); + hci->send_low_power_command(enable ? LPM_ENABLE : LPM_DISABLE); } /****************************************************************************** @@ -440,12 +445,7 @@ void bte_main_enable_lpm(BOOLEAN enable) ******************************************************************************/ void bte_main_lpm_allow_bt_device_sleep() { - int result = -1; - - if ((bt_hc_if) && (lpm_enabled == TRUE)) - result = bt_hc_if->lpm(BT_HC_LPM_WAKE_DEASSERT); - - APPL_TRACE_DEBUG("HC lib lpm deassertion return %d", result); + hci->send_low_power_command(LPM_WAKE_DEASSERT); } /****************************************************************************** @@ -459,12 +459,7 @@ void bte_main_lpm_allow_bt_device_sleep() ******************************************************************************/ void bte_main_lpm_wake_bt_device() { - int result = -1; - - if ((bt_hc_if) && (lpm_enabled == TRUE)) - result = bt_hc_if->lpm(BT_HC_LPM_WAKE_ASSERT); - - APPL_TRACE_DEBUG("HC lib lpm assertion return %d", result); + hci->send_low_power_command(LPM_WAKE_ASSERT); } #endif // HCILP_INCLUDED @@ -520,10 +515,7 @@ int set_audio_state(UINT16 handle, UINT16 codec, UINT8 state, void *param) /* layer_specific shall contain return path event! for BTA events! * 0 means no return message is expected. */ p_msg->hdr.layer_specific = 0; - if (bt_hc_if) - { - bt_hc_if->tx_cmd((TRANSAC)p_msg, (char *)(&p_msg->audio), sizeof(*p_msg)); - } + hci->transmit_downward(MSG_STACK_TO_HC_HCI_CMD, p_msg); return result; } @@ -550,12 +542,7 @@ void bte_main_hci_send (BT_HDR *p_msg, UINT16 event) if((sub_event == LOCAL_BR_EDR_CONTROLLER_ID) || \ (sub_event == LOCAL_BLE_CONTROLLER_ID)) { - if (bt_hc_if) - bt_hc_if->transmit_buf((TRANSAC)p_msg, \ - (char *) (p_msg + 1), \ - p_msg->len); - else - GKI_freebuf(p_msg); + hci->transmit_downward(event, p_msg); } else { @@ -595,13 +582,11 @@ void bte_main_post_reset_init() ** Returns None ** ******************************************************************************/ -static void preload_cb(TRANSAC transac, bt_hc_preload_result_t result) +static void preload_cb(bool success) { - UNUSED(transac); - - APPL_TRACE_EVENT("HC preload_cb %d [0:SUCCESS 1:FAIL]", result); + APPL_TRACE_EVENT("HC preload_cb %d [1:SUCCESS 0:FAIL]", success); - if (result == BT_HC_PRELOAD_SUCCESS) + if (success) { preload_stop_wait_timer(); @@ -612,54 +597,6 @@ static void preload_cb(TRANSAC transac, bt_hc_preload_result_t result) /****************************************************************************** ** -** Function postload_cb -** -** Description HOST/CONTROLLER LIB CALLBACK API - This function is called -** when the libbt-hci lib completed stack postload process -** -** Returns None -** -******************************************************************************/ -static void postload_cb(TRANSAC transac, bt_hc_postload_result_t result) -{ - UNUSED(transac); - - APPL_TRACE_EVENT("HC postload_cb %d", result); -} - -/****************************************************************************** -** -** Function lpm_cb -** -** Description HOST/CONTROLLER LIB CALLBACK API - This function is called -** back from the libbt-hci to indicate the current LPM state -** -** Returns None -** -******************************************************************************/ -static void lpm_cb(bt_hc_lpm_request_result_t result) -{ - APPL_TRACE_EVENT("HC lpm_result_cb %d", result); - lpm_enabled = (result == BT_HC_LPM_ENABLED) ? TRUE : FALSE; -} - -/****************************************************************************** -** -** Function hostwake_ind -** -** Description HOST/CONTROLLER LIB CALLOUT API - This function is called -** from the libbt-hci to indicate the HostWake event -** -** Returns None -** -******************************************************************************/ -static void hostwake_ind(bt_hc_low_power_event_t event) -{ - APPL_TRACE_EVENT("HC hostwake_ind %d", event); -} - -/****************************************************************************** -** ** Function alloc ** ** Description HOST/CONTROLLER LIB CALLOUT API - This function is called @@ -668,14 +605,8 @@ static void hostwake_ind(bt_hc_low_power_event_t event) ** Returns NULL / pointer to allocated buffer ** ******************************************************************************/ -static char *alloc(int size) +static void *alloc(size_t size) { - BT_HDR *p_hdr = NULL; - - /* - APPL_TRACE_DEBUG("HC alloc size=%d", size); - */ - /* Requested buffer size cannot exceed GKI_MAX_BUF_SIZE. */ if (size > GKI_MAX_BUF_SIZE) { @@ -684,14 +615,14 @@ static char *alloc(int size) return NULL; } - p_hdr = (BT_HDR *) GKI_getbuf ((UINT16) size); + BT_HDR *p_hdr = (BT_HDR *) GKI_getbuf ((UINT16) size); - if (p_hdr == NULL) + if (!p_hdr) { APPL_TRACE_WARNING("alloc returns NO BUFFER! (sz %d)", size); } - return ((char *) p_hdr); + return p_hdr; } /****************************************************************************** @@ -707,43 +638,13 @@ static char *alloc(int size) ** boundary. ** ******************************************************************************/ -static void dealloc(TRANSAC transac) +static void dealloc(void *buffer) { - GKI_freebuf(transac); + GKI_freebuf(buffer); } -/****************************************************************************** -** -** Function data_ind -** -** Description HOST/CONTROLLER LIB CALLOUT API - This function is called -** from the libbt-hci to pass in the received HCI packets -** -** The core stack is responsible for releasing the data buffer -** passed in from the libbt-hci once the core stack has done -** with it. -** -** Bluedroid libbt-hci library uses 'transac' parameter to -** pass data-path buffer/packet across bt_hci_lib interface -** boundary. The 'p_buf' and 'len' parameters are not intended -** to be used here but might point to data portion in data- -** path buffer and length of valid data respectively. -** -** Returns bt_hc_status_t -** -******************************************************************************/ -static int data_ind(TRANSAC transac, char *p_buf, int len) -{ - BT_HDR *p_msg = (BT_HDR *) transac; - UNUSED(p_buf); - UNUSED(len); - - /* - APPL_TRACE_DEBUG("HC data_ind event=0x%04X (len=%d)", p_msg->event, len); - */ - - GKI_send_msg (BTU_TASK, BTU_HCI_RCV_MBOX, transac); - return BT_HC_STATUS_SUCCESS; +static void dump_upbound_data_to_btu(fixed_queue_t *queue, UNUSED_ATTR void *context) { + GKI_send_msg (BTU_TASK, BTU_HCI_RCV_MBOX, fixed_queue_dequeue(queue)); } /****************************************************************************** @@ -766,38 +667,28 @@ static int data_ind(TRANSAC transac, char *p_buf, int len) ** Returns bt_hc_status_t ** ******************************************************************************/ -static int tx_result(TRANSAC transac, char *p_buf, bt_hc_transmit_result_t result) +static void tx_result(void *p_buf, bool all_fragments_sent) { - UNUSED(p_buf); - /* - APPL_TRACE_DEBUG("HC tx_result %d (event=%04X)", result, \ - ((BT_HDR *)transac)->event); - */ - - if (result == BT_HC_TX_FRAGMENT) + if (!all_fragments_sent) { - GKI_send_msg (BTU_TASK, BTU_HCI_RCV_MBOX, transac); + GKI_send_msg (BTU_TASK, BTU_HCI_RCV_MBOX, p_buf); } else { - GKI_freebuf(transac); + GKI_freebuf(p_buf); } - - return BT_HC_STATUS_SUCCESS; } /***************************************************************************** ** The libbt-hci Callback Functions Table *****************************************************************************/ -static const bt_hc_callbacks_t hc_callbacks = { - sizeof(bt_hc_callbacks_t), +static const hci_callbacks_t hci_callbacks = { preload_cb, - postload_cb, - lpm_cb, - hostwake_ind, - alloc, - dealloc, - data_ind, tx_result }; +static const allocator_t buffer_allocator = { + alloc, + dealloc +}; + |