From 917efb1c0e2458434390463ca11bd9be935c2e54 Mon Sep 17 00:00:00 2001 From: Zach Johnson Date: Sun, 26 Feb 2017 23:46:05 -0800 Subject: [PATCH] Bring multi-channel transport into the glorious new age Some devices still use MCT as their UART protocol, so we need to bring it forward to the fresh new HAL. Also, adding tests for H4 while I'm here. Test: new unit tests pass and a device using MCT now boots BT again \o/ Fixes: 34992730 Change-Id: Idb8e536a2779929ad8a0d4bac492c3011995cd79 --- bluetooth/1.0/default/Android.bp | 8 + bluetooth/1.0/default/bluetooth_hci.cc | 23 +- bluetooth/1.0/default/h4_protocol.cc | 71 ++++++ bluetooth/1.0/default/h4_protocol.h | 61 +++++ bluetooth/1.0/default/hci_packetizer.cc | 72 ++---- bluetooth/1.0/default/hci_packetizer.h | 21 +- bluetooth/1.0/default/hci_protocol.cc | 74 ++++++ bluetooth/1.0/default/hci_protocol.h | 49 ++++ bluetooth/1.0/default/mct_protocol.cc | 71 ++++++ bluetooth/1.0/default/mct_protocol.h | 56 +++++ .../1.0/default/test/h4_protocol_unittest.cc | 213 ++++++++++++++++++ .../1.0/default/test/mct_protocol_unittest.cc | 200 ++++++++++++++++ bluetooth/1.0/default/vendor_interface.cc | 122 +++++----- bluetooth/1.0/default/vendor_interface.h | 20 +- 14 files changed, 907 insertions(+), 154 deletions(-) create mode 100644 bluetooth/1.0/default/h4_protocol.cc create mode 100644 bluetooth/1.0/default/h4_protocol.h create mode 100644 bluetooth/1.0/default/hci_protocol.cc create mode 100644 bluetooth/1.0/default/hci_protocol.h create mode 100644 bluetooth/1.0/default/mct_protocol.cc create mode 100644 bluetooth/1.0/default/mct_protocol.h create mode 100644 bluetooth/1.0/default/test/h4_protocol_unittest.cc create mode 100644 bluetooth/1.0/default/test/mct_protocol_unittest.cc diff --git a/bluetooth/1.0/default/Android.bp b/bluetooth/1.0/default/Android.bp index fb20195c0e..e04c2f4c1b 100644 --- a/bluetooth/1.0/default/Android.bp +++ b/bluetooth/1.0/default/Android.bp @@ -56,6 +56,9 @@ cc_library_static { name: "android.hardware.bluetooth-hci", srcs: [ "hci_packetizer.cc", + "hci_protocol.cc", + "h4_protocol.cc", + "mct_protocol.cc", ], export_include_dirs: ["."], shared_libs: [ @@ -71,16 +74,21 @@ cc_test { name: "bluetooth-vendor-interface-unit-tests", srcs: [ "test/async_fd_watcher_unittest.cc", + "test/h4_protocol_unittest.cc", + "test/mct_protocol_unittest.cc", ], local_include_dirs: [ "test", ], shared_libs: [ "libbase", + "libhidlbase", "liblog", ], static_libs: [ "android.hardware.bluetooth-async", + "android.hardware.bluetooth-hci", + "libgmock", ], } diff --git a/bluetooth/1.0/default/bluetooth_hci.cc b/bluetooth/1.0/default/bluetooth_hci.cc index 1d6e600ba0..5a282bf2a3 100644 --- a/bluetooth/1.0/default/bluetooth_hci.cc +++ b/bluetooth/1.0/default/bluetooth_hci.cc @@ -44,21 +44,14 @@ Return BluetoothHci::initialize( event_cb_->initializationComplete( status ? Status::SUCCESS : Status::INITIALIZATION_ERROR); }, - [this](HciPacketType type, const hidl_vec& packet) { - switch (type) { - case HCI_PACKET_TYPE_EVENT: - event_cb_->hciEventReceived(packet); - break; - case HCI_PACKET_TYPE_ACL_DATA: - event_cb_->aclDataReceived(packet); - break; - case HCI_PACKET_TYPE_SCO_DATA: - event_cb_->scoDataReceived(packet); - break; - default: - ALOGE("%s Unexpected event type %d", __func__, type); - break; - } + [this](const hidl_vec& packet) { + event_cb_->hciEventReceived(packet); + }, + [this](const hidl_vec& packet) { + event_cb_->aclDataReceived(packet); + }, + [this](const hidl_vec& packet) { + event_cb_->scoDataReceived(packet); }); if (!rc) event_cb_->initializationComplete(Status::INITIALIZATION_ERROR); return Void(); diff --git a/bluetooth/1.0/default/h4_protocol.cc b/bluetooth/1.0/default/h4_protocol.cc new file mode 100644 index 0000000000..8f24b5eeac --- /dev/null +++ b/bluetooth/1.0/default/h4_protocol.cc @@ -0,0 +1,71 @@ +// +// Copyright 2017 The Android Open Source Project +// +// 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 "h4_protocol.h" + +#define LOG_TAG "android.hardware.bluetooth-hci-h4" +#include +#include +#include + +namespace android { +namespace hardware { +namespace bluetooth { +namespace hci { + +size_t H4Protocol::Send(uint8_t type, const uint8_t* data, size_t length) { + int rv = WriteSafely(uart_fd_, &type, sizeof(type)); + if (rv == sizeof(type)) { + rv = WriteSafely(uart_fd_, data, length); + } + return rv; +} + +void H4Protocol::OnPacketReady() { + switch (hci_packet_type_) { + case HCI_PACKET_TYPE_EVENT: + event_cb_(hci_packetizer_.GetPacket()); + break; + case HCI_PACKET_TYPE_ACL_DATA: + acl_cb_(hci_packetizer_.GetPacket()); + break; + case HCI_PACKET_TYPE_SCO_DATA: + sco_cb_(hci_packetizer_.GetPacket()); + break; + default: { + bool bad_packet_type = true; + CHECK(!bad_packet_type); + } + } + // Get ready for the next type byte. + hci_packet_type_ = HCI_PACKET_TYPE_UNKNOWN; +} + +void H4Protocol::OnDataReady(int fd) { + if (hci_packet_type_ == HCI_PACKET_TYPE_UNKNOWN) { + uint8_t buffer[1] = {0}; + size_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, 1)); + CHECK(bytes_read == 1); + hci_packet_type_ = static_cast(buffer[0]); + } else { + hci_packetizer_.OnDataReady(fd, hci_packet_type_); + } +} + +} // namespace hci +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/1.0/default/h4_protocol.h b/bluetooth/1.0/default/h4_protocol.h new file mode 100644 index 0000000000..0d0a1caf19 --- /dev/null +++ b/bluetooth/1.0/default/h4_protocol.h @@ -0,0 +1,61 @@ +// +// Copyright 2017 The Android Open Source Project +// +// 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 + +#include "async_fd_watcher.h" +#include "bt_vendor_lib.h" +#include "hci_internals.h" +#include "hci_protocol.h" + +namespace android { +namespace hardware { +namespace bluetooth { +namespace hci { + +class H4Protocol : public HciProtocol { + public: + H4Protocol(int fd, PacketReadCallback event_cb, PacketReadCallback acl_cb, + PacketReadCallback sco_cb) + : uart_fd_(fd), + event_cb_(event_cb), + acl_cb_(acl_cb), + sco_cb_(sco_cb), + hci_packetizer_([this]() { OnPacketReady(); }) {} + + size_t Send(uint8_t type, const uint8_t* data, size_t length); + + void OnPacketReady(); + + void OnDataReady(int fd); + + private: + int uart_fd_; + + PacketReadCallback event_cb_; + PacketReadCallback acl_cb_; + PacketReadCallback sco_cb_; + + HciPacketType hci_packet_type_{HCI_PACKET_TYPE_UNKNOWN}; + hci::HciPacketizer hci_packetizer_; +}; + +} // namespace hci +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/1.0/default/hci_packetizer.cc b/bluetooth/1.0/default/hci_packetizer.cc index 1a5019600f..9549858b2c 100644 --- a/bluetooth/1.0/default/hci_packetizer.cc +++ b/bluetooth/1.0/default/hci_packetizer.cc @@ -18,7 +18,6 @@ #define LOG_TAG "android.hardware.bluetooth.hci_packetizer" #include -#include #include #include @@ -46,63 +45,40 @@ namespace hardware { namespace bluetooth { namespace hci { -HciPacketType HciPacketizer::GetPacketType() const { - return hci_packet_type_; -} +const hidl_vec& HciPacketizer::GetPacket() const { return packet_; } -const hidl_vec& HciPacketizer::GetPacket() const { - return hci_packet_; -} - -void HciPacketizer::OnDataReady(int fd) { - switch (hci_parser_state_) { - case HCI_IDLE: { - uint8_t buffer[1] = {0}; - size_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, 1)); - CHECK(bytes_read == 1); - hci_packet_type_ = static_cast(buffer[0]); - CHECK(hci_packet_type_ >= HCI_PACKET_TYPE_ACL_DATA && - hci_packet_type_ <= HCI_PACKET_TYPE_EVENT) - << "buffer[0] = " << static_cast(buffer[0]); - hci_parser_state_ = HCI_TYPE_READY; - hci_packet_bytes_remaining_ = preamble_size_for_type[hci_packet_type_]; - hci_packet_bytes_read_ = 0; - break; - } - - case HCI_TYPE_READY: { +void HciPacketizer::OnDataReady(int fd, HciPacketType packet_type) { + switch (state_) { + case HCI_PREAMBLE: { size_t bytes_read = TEMP_FAILURE_RETRY( - read(fd, hci_packet_preamble_ + hci_packet_bytes_read_, - hci_packet_bytes_remaining_)); + read(fd, preamble_ + bytes_read_, + preamble_size_for_type[packet_type] - bytes_read_)); CHECK(bytes_read > 0); - hci_packet_bytes_remaining_ -= bytes_read; - hci_packet_bytes_read_ += bytes_read; - if (hci_packet_bytes_remaining_ == 0) { + bytes_read_ += bytes_read; + if (bytes_read_ == preamble_size_for_type[packet_type]) { size_t packet_length = - HciGetPacketLengthForType(hci_packet_type_, hci_packet_preamble_); - hci_packet_.resize(preamble_size_for_type[hci_packet_type_] + - packet_length); - memcpy(hci_packet_.data(), hci_packet_preamble_, - preamble_size_for_type[hci_packet_type_]); - hci_packet_bytes_remaining_ = packet_length; - hci_parser_state_ = HCI_PAYLOAD; - hci_packet_bytes_read_ = 0; + HciGetPacketLengthForType(packet_type, preamble_); + packet_.resize(preamble_size_for_type[packet_type] + packet_length); + memcpy(packet_.data(), preamble_, preamble_size_for_type[packet_type]); + bytes_remaining_ = packet_length; + state_ = HCI_PAYLOAD; + bytes_read_ = 0; } break; } case HCI_PAYLOAD: { - size_t bytes_read = TEMP_FAILURE_RETRY( - read(fd, - hci_packet_.data() + preamble_size_for_type[hci_packet_type_] + - hci_packet_bytes_read_, - hci_packet_bytes_remaining_)); + size_t bytes_read = TEMP_FAILURE_RETRY(read( + fd, + packet_.data() + preamble_size_for_type[packet_type] + bytes_read_, + bytes_remaining_)); CHECK(bytes_read > 0); - hci_packet_bytes_remaining_ -= bytes_read; - hci_packet_bytes_read_ += bytes_read; - if (hci_packet_bytes_remaining_ == 0) { - hci_packet_ready_cb_(); - hci_parser_state_ = HCI_IDLE; + bytes_remaining_ -= bytes_read; + bytes_read_ += bytes_read; + if (bytes_remaining_ == 0) { + packet_ready_cb_(); + state_ = HCI_PREAMBLE; + bytes_read_ = 0; } break; } diff --git a/bluetooth/1.0/default/hci_packetizer.h b/bluetooth/1.0/default/hci_packetizer.h index e9c01dcf2c..90579bd2a7 100644 --- a/bluetooth/1.0/default/hci_packetizer.h +++ b/bluetooth/1.0/default/hci_packetizer.h @@ -32,20 +32,19 @@ using HciPacketReadyCallback = std::function; class HciPacketizer { public: - HciPacketizer(HciPacketReadyCallback packet_cb) : hci_packet_ready_cb_(packet_cb) {}; - void OnDataReady(int fd); + HciPacketizer(HciPacketReadyCallback packet_cb) + : packet_ready_cb_(packet_cb){}; + void OnDataReady(int fd, HciPacketType packet_type); const hidl_vec& GetPacket() const; - HciPacketType GetPacketType() const; protected: - enum HciParserState { HCI_IDLE, HCI_TYPE_READY, HCI_PAYLOAD }; - HciParserState hci_parser_state_{HCI_IDLE}; - HciPacketType hci_packet_type_{HCI_PACKET_TYPE_UNKNOWN}; - uint8_t hci_packet_preamble_[HCI_PREAMBLE_SIZE_MAX]; - hidl_vec hci_packet_; - size_t hci_packet_bytes_remaining_; - size_t hci_packet_bytes_read_; - HciPacketReadyCallback hci_packet_ready_cb_; + enum State { HCI_PREAMBLE, HCI_PAYLOAD }; + State state_{HCI_PREAMBLE}; + uint8_t preamble_[HCI_PREAMBLE_SIZE_MAX]; + hidl_vec packet_; + size_t bytes_remaining_{0}; + size_t bytes_read_{0}; + HciPacketReadyCallback packet_ready_cb_; }; } // namespace hci diff --git a/bluetooth/1.0/default/hci_protocol.cc b/bluetooth/1.0/default/hci_protocol.cc new file mode 100644 index 0000000000..cd709b43ff --- /dev/null +++ b/bluetooth/1.0/default/hci_protocol.cc @@ -0,0 +1,74 @@ +// +// Copyright 2017 The Android Open Source Project +// +// 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 "hci_protocol.h" + +#define LOG_TAG "android.hardware.bluetooth-hci-hci_protocol" +#include +#include +#include +#include + +namespace { + +const size_t preamble_size_for_type[] = { + 0, HCI_COMMAND_PREAMBLE_SIZE, HCI_ACL_PREAMBLE_SIZE, HCI_SCO_PREAMBLE_SIZE, + HCI_EVENT_PREAMBLE_SIZE}; +const size_t packet_length_offset_for_type[] = { + 0, HCI_LENGTH_OFFSET_CMD, HCI_LENGTH_OFFSET_ACL, HCI_LENGTH_OFFSET_SCO, + HCI_LENGTH_OFFSET_EVT}; + +size_t HciGetPacketLengthForType(HciPacketType type, const uint8_t* preamble) { + size_t offset = packet_length_offset_for_type[type]; + if (type != HCI_PACKET_TYPE_ACL_DATA) return preamble[offset]; + return (((preamble[offset + 1]) << 8) | preamble[offset]); +} + +} // namespace + +namespace android { +namespace hardware { +namespace bluetooth { +namespace hci { + +size_t HciProtocol::WriteSafely(int fd, const uint8_t* data, size_t length) { + size_t transmitted_length = 0; + while (length > 0) { + ssize_t ret = + TEMP_FAILURE_RETRY(write(fd, data + transmitted_length, length)); + + if (ret == -1) { + if (errno == EAGAIN) continue; + ALOGE("%s error writing to UART (%s)", __func__, strerror(errno)); + break; + + } else if (ret == 0) { + // Nothing written :( + ALOGE("%s zero bytes written - something went wrong...", __func__); + break; + } + + transmitted_length += ret; + length -= ret; + } + + return transmitted_length; +} + +} // namespace hci +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/1.0/default/hci_protocol.h b/bluetooth/1.0/default/hci_protocol.h new file mode 100644 index 0000000000..6821107166 --- /dev/null +++ b/bluetooth/1.0/default/hci_protocol.h @@ -0,0 +1,49 @@ +// +// Copyright 2017 The Android Open Source Project +// +// 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 + +#include "bt_vendor_lib.h" +#include "hci_internals.h" +#include "hci_packetizer.h" + +namespace android { +namespace hardware { +namespace bluetooth { +namespace hci { + +using ::android::hardware::hidl_vec; +using PacketReadCallback = std::function&)>; + +// Implementation of HCI protocol bits common to different transports +class HciProtocol { + public: + HciProtocol() = default; + virtual ~HciProtocol(){}; + + // Protocol-specific implementation of sending packets. + virtual size_t Send(uint8_t type, const uint8_t* data, size_t length) = 0; + + protected: + static size_t WriteSafely(int fd, const uint8_t* data, size_t length); +}; + +} // namespace hci +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/1.0/default/mct_protocol.cc b/bluetooth/1.0/default/mct_protocol.cc new file mode 100644 index 0000000000..813cebd239 --- /dev/null +++ b/bluetooth/1.0/default/mct_protocol.cc @@ -0,0 +1,71 @@ +// +// Copyright 2017 The Android Open Source Project +// +// 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 "mct_protocol.h" + +#include + +#define LOG_TAG "android.hardware.bluetooth-hci-mct" +#include +#include + +#include + +namespace android { +namespace hardware { +namespace bluetooth { +namespace hci { + +MctProtocol::MctProtocol(int* fds, PacketReadCallback event_cb, + PacketReadCallback acl_cb) + : event_cb_(event_cb), + acl_cb_(acl_cb), + event_packetizer_([this]() { OnEventPacketReady(); }), + acl_packetizer_([this]() { OnAclDataPacketReady(); }) { + for (int i = 0; i < CH_MAX; i++) { + uart_fds_[i] = fds[i]; + } +} + +size_t MctProtocol::Send(uint8_t type, const uint8_t* data, size_t length) { + if (type == HCI_PACKET_TYPE_COMMAND) + return WriteSafely(uart_fds_[CH_CMD], data, length); + if (type == HCI_PACKET_TYPE_ACL_DATA) + return WriteSafely(uart_fds_[CH_ACL_OUT], data, length); + CHECK(type == HCI_PACKET_TYPE_COMMAND || type == HCI_PACKET_TYPE_ACL_DATA); + return 0; +} + +void MctProtocol::OnEventPacketReady() { + event_cb_(event_packetizer_.GetPacket()); +} + +void MctProtocol::OnAclDataPacketReady() { + acl_cb_(acl_packetizer_.GetPacket()); +} + +void MctProtocol::OnEventDataReady(int fd) { + event_packetizer_.OnDataReady(fd, HCI_PACKET_TYPE_EVENT); +} + +void MctProtocol::OnAclDataReady(int fd) { + acl_packetizer_.OnDataReady(fd, HCI_PACKET_TYPE_ACL_DATA); +} + +} // namespace hci +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/1.0/default/mct_protocol.h b/bluetooth/1.0/default/mct_protocol.h new file mode 100644 index 0000000000..6991746248 --- /dev/null +++ b/bluetooth/1.0/default/mct_protocol.h @@ -0,0 +1,56 @@ +// +// Copyright 2017 The Android Open Source Project +// +// 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 + +#include "async_fd_watcher.h" +#include "bt_vendor_lib.h" +#include "hci_internals.h" +#include "hci_protocol.h" + +namespace android { +namespace hardware { +namespace bluetooth { +namespace hci { + +class MctProtocol : public HciProtocol { + public: + MctProtocol(int* fds, PacketReadCallback event_cb, PacketReadCallback acl_cb); + + size_t Send(uint8_t type, const uint8_t* data, size_t length); + + void OnEventPacketReady(); + void OnAclDataPacketReady(); + + void OnEventDataReady(int fd); + void OnAclDataReady(int fd); + + private: + int uart_fds_[CH_MAX]; + + PacketReadCallback event_cb_; + PacketReadCallback acl_cb_; + + hci::HciPacketizer event_packetizer_; + hci::HciPacketizer acl_packetizer_; +}; + +} // namespace hci +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/1.0/default/test/h4_protocol_unittest.cc b/bluetooth/1.0/default/test/h4_protocol_unittest.cc new file mode 100644 index 0000000000..d53aaa9fac --- /dev/null +++ b/bluetooth/1.0/default/test/h4_protocol_unittest.cc @@ -0,0 +1,213 @@ +// +// Copyright 2017 The Android Open Source Project +// +// 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_h4_unittest" + +#include "h4_protocol.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace android { +namespace hardware { +namespace bluetooth { +namespace V1_0 { +namespace implementation { + +using ::testing::Eq; +using hci::H4Protocol; + +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."; + +MATCHER_P3(HidlVecMatches, preamble, preamble_length, payload, "") { + size_t length = strlen(payload) + preamble_length; + if (length != arg.size()) { + return false; + } + + if (memcmp(preamble, arg.data(), preamble_length) != 0) { + return false; + } + + return memcmp(payload, arg.data() + preamble_length, + length - preamble_length) == 0; +}; + +ACTION_P2(Notify, mutex, condition) { + ALOGD("%s", __func__); + std::unique_lock lock(*mutex); + condition->notify_one(); +} + +class H4ProtocolTest : public ::testing::Test { + protected: + void SetUp() override { + ALOGD("%s", __func__); + + int sockfd[2]; + socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd); + H4Protocol* h4_hci = + new H4Protocol(sockfd[0], event_cb_.AsStdFunction(), + acl_cb_.AsStdFunction(), sco_cb_.AsStdFunction()); + fd_watcher_.WatchFdForNonBlockingReads( + sockfd[0], [h4_hci](int fd) { h4_hci->OnDataReady(fd); }); + protocol_ = h4_hci; + + fake_uart_ = sockfd[1]; + } + + void TearDown() override { fd_watcher_.StopWatchingFileDescriptors(); } + + void SendAndReadUartOutbound(uint8_t type, char* data) { + ALOGD("%s sending", __func__); + int data_length = strlen(data); + protocol_->Send(type, (uint8_t*)data, data_length); + + int uart_length = data_length + 1; // + 1 for data type code + int i; + + ALOGD("%s reading", __func__); + for (i = 0; i < uart_length; i++) { + fd_set read_fds; + FD_ZERO(&read_fds); + FD_SET(fake_uart_, &read_fds); + TEMP_FAILURE_RETRY(select(fake_uart_ + 1, &read_fds, NULL, NULL, NULL)); + + char byte; + TEMP_FAILURE_RETRY(read(fake_uart_, &byte, 1)); + + EXPECT_EQ(i == 0 ? type : data[i - 1], byte); + } + + EXPECT_EQ(i, uart_length); + } + + void WriteAndExpectInboundAclData(char* payload) { + // h4 type[1] + handle[2] + size[2] + char preamble[5] = {HCI_PACKET_TYPE_ACL_DATA, 19, 92, 0, 0}; + int length = strlen(payload); + preamble[3] = length & 0xFF; + preamble[4] = (length >> 8) & 0xFF; + + ALOGD("%s writing", __func__); + TEMP_FAILURE_RETRY(write(fake_uart_, preamble, sizeof(preamble))); + TEMP_FAILURE_RETRY(write(fake_uart_, payload, strlen(payload))); + + ALOGD("%s waiting", __func__); + std::mutex mutex; + std::condition_variable done; + EXPECT_CALL(acl_cb_, Call(HidlVecMatches(preamble + 1, sizeof(preamble) - 1, + payload))) + .WillOnce(Notify(&mutex, &done)); + + // Fail if it takes longer than 100 ms. + auto timeout_time = + std::chrono::steady_clock::now() + std::chrono::milliseconds(100); + { + std::unique_lock lock(mutex); + done.wait_until(lock, timeout_time); + } + } + + void WriteAndExpectInboundScoData(char* payload) { + // h4 type[1] + handle[2] + size[1] + char preamble[4] = {HCI_PACKET_TYPE_SCO_DATA, 20, 17, 0}; + preamble[3] = strlen(payload) & 0xFF; + + ALOGD("%s writing", __func__); + TEMP_FAILURE_RETRY(write(fake_uart_, preamble, sizeof(preamble))); + TEMP_FAILURE_RETRY(write(fake_uart_, payload, strlen(payload))); + + ALOGD("%s waiting", __func__); + std::mutex mutex; + std::condition_variable done; + EXPECT_CALL(sco_cb_, Call(HidlVecMatches(preamble + 1, sizeof(preamble) - 1, + payload))) + .WillOnce(Notify(&mutex, &done)); + + // Fail if it takes longer than 100 ms. + auto timeout_time = + std::chrono::steady_clock::now() + std::chrono::milliseconds(100); + { + std::unique_lock lock(mutex); + done.wait_until(lock, timeout_time); + } + } + + void WriteAndExpectInboundEvent(char* payload) { + // h4 type[1] + event_code[1] + size[1] + char preamble[3] = {HCI_PACKET_TYPE_EVENT, 9, 0}; + preamble[2] = strlen(payload) & 0xFF; + ALOGD("%s writing", __func__); + TEMP_FAILURE_RETRY(write(fake_uart_, preamble, sizeof(preamble))); + TEMP_FAILURE_RETRY(write(fake_uart_, payload, strlen(payload))); + + ALOGD("%s waiting", __func__); + std::mutex mutex; + std::condition_variable done; + EXPECT_CALL(event_cb_, Call(HidlVecMatches(preamble + 1, + sizeof(preamble) - 1, payload))) + .WillOnce(Notify(&mutex, &done)); + + { + std::unique_lock lock(mutex); + done.wait(lock); + } + } + + testing::MockFunction&)> event_cb_; + testing::MockFunction&)> acl_cb_; + testing::MockFunction&)> sco_cb_; + async::AsyncFdWatcher fd_watcher_; + H4Protocol* protocol_; + int fake_uart_; +}; + +// Test sending data sends correct data onto the UART +TEST_F(H4ProtocolTest, TestSends) { + SendAndReadUartOutbound(HCI_PACKET_TYPE_COMMAND, sample_data1); + SendAndReadUartOutbound(HCI_PACKET_TYPE_ACL_DATA, sample_data2); + SendAndReadUartOutbound(HCI_PACKET_TYPE_SCO_DATA, sample_data3); +} + +// Ensure we properly parse data coming from the UART +TEST_F(H4ProtocolTest, TestReads) { + WriteAndExpectInboundAclData(acl_data); + WriteAndExpectInboundScoData(sco_data); + WriteAndExpectInboundEvent(event_data); +} + +} // namespace implementation +} // namespace V1_0 +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/1.0/default/test/mct_protocol_unittest.cc b/bluetooth/1.0/default/test/mct_protocol_unittest.cc new file mode 100644 index 0000000000..5751a5e394 --- /dev/null +++ b/bluetooth/1.0/default/test/mct_protocol_unittest.cc @@ -0,0 +1,200 @@ +// +// Copyright 2017 The Android Open Source Project +// +// 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_h4_unittest" + +#include "mct_protocol.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace android { +namespace hardware { +namespace bluetooth { +namespace V1_0 { +namespace implementation { + +using ::testing::Eq; +using hci::MctProtocol; + +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."; + +MATCHER_P3(HidlVecMatches, preamble, preamble_length, payload, "") { + size_t length = strlen(payload) + preamble_length; + if (length != arg.size()) { + return false; + } + + if (memcmp(preamble, arg.data(), preamble_length) != 0) { + return false; + } + + return memcmp(payload, arg.data() + preamble_length, + length - preamble_length) == 0; +}; + +ACTION_P2(Notify, mutex, condition) { + ALOGD("%s", __func__); + std::unique_lock lock(*mutex); + condition->notify_one(); +} + +class MctProtocolTest : public ::testing::Test { + protected: + void SetUp() override { + ALOGD("%s", __func__); + + int mct_fds[CH_MAX]; + MakeFakeUartFd(CH_CMD, mct_fds, fake_uart_); + MakeFakeUartFd(CH_EVT, mct_fds, fake_uart_); + MakeFakeUartFd(CH_ACL_IN, mct_fds, fake_uart_); + MakeFakeUartFd(CH_ACL_OUT, mct_fds, fake_uart_); + + MctProtocol* mct_hci = new MctProtocol(mct_fds, event_cb_.AsStdFunction(), + acl_cb_.AsStdFunction()); + fd_watcher_.WatchFdForNonBlockingReads( + mct_fds[CH_EVT], [mct_hci](int fd) { mct_hci->OnEventDataReady(fd); }); + fd_watcher_.WatchFdForNonBlockingReads( + mct_fds[CH_ACL_IN], [mct_hci](int fd) { mct_hci->OnAclDataReady(fd); }); + protocol_ = mct_hci; + } + + void MakeFakeUartFd(int index, int* host_side, int* controller_side) { + int sockfd[2]; + socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd); + host_side[index] = sockfd[0]; + controller_side[index] = sockfd[1]; + } + + void TearDown() override { fd_watcher_.StopWatchingFileDescriptors(); } + + void SendAndReadUartOutbound(uint8_t type, char* data, int outbound_fd) { + ALOGD("%s sending", __func__); + int data_length = strlen(data); + protocol_->Send(type, (uint8_t*)data, data_length); + + ALOGD("%s reading", __func__); + int i; + for (i = 0; i < data_length; i++) { + fd_set read_fds; + FD_ZERO(&read_fds); + FD_SET(outbound_fd, &read_fds); + TEMP_FAILURE_RETRY(select(outbound_fd + 1, &read_fds, NULL, NULL, NULL)); + + char byte; + TEMP_FAILURE_RETRY(read(outbound_fd, &byte, 1)); + + EXPECT_EQ(data[i], byte); + } + + EXPECT_EQ(i, data_length); + } + + void WriteAndExpectInboundAclData(char* payload) { + // handle[2] + size[2] + char preamble[4] = {19, 92, 0, 0}; + int length = strlen(payload); + preamble[2] = length & 0xFF; + preamble[3] = (length >> 8) & 0xFF; + + ALOGD("%s writing", __func__); + TEMP_FAILURE_RETRY( + write(fake_uart_[CH_ACL_IN], preamble, sizeof(preamble))); + TEMP_FAILURE_RETRY(write(fake_uart_[CH_ACL_IN], payload, strlen(payload))); + + ALOGD("%s waiting", __func__); + std::mutex mutex; + std::condition_variable done; + EXPECT_CALL(acl_cb_, + Call(HidlVecMatches(preamble, sizeof(preamble), payload))) + .WillOnce(Notify(&mutex, &done)); + + // Fail if it takes longer than 100 ms. + auto timeout_time = + std::chrono::steady_clock::now() + std::chrono::milliseconds(100); + { + std::unique_lock lock(mutex); + done.wait_until(lock, timeout_time); + } + } + + void WriteAndExpectInboundEvent(char* payload) { + // event_code[1] + size[1] + char preamble[2] = {9, 0}; + preamble[1] = strlen(payload) & 0xFF; + + ALOGD("%s writing", __func__); + TEMP_FAILURE_RETRY(write(fake_uart_[CH_EVT], preamble, sizeof(preamble))); + TEMP_FAILURE_RETRY(write(fake_uart_[CH_EVT], payload, strlen(payload))); + + ALOGD("%s waiting", __func__); + std::mutex mutex; + std::condition_variable done; + EXPECT_CALL(event_cb_, + Call(HidlVecMatches(preamble, sizeof(preamble), payload))) + .WillOnce(Notify(&mutex, &done)); + + // Fail if it takes longer than 100 ms. + auto timeout_time = + std::chrono::steady_clock::now() + std::chrono::milliseconds(100); + { + std::unique_lock lock(mutex); + done.wait_until(lock, timeout_time); + } + } + + testing::MockFunction&)> event_cb_; + testing::MockFunction&)> acl_cb_; + async::AsyncFdWatcher fd_watcher_; + MctProtocol* protocol_; + int fake_uart_[CH_MAX]; +}; + +// Test sending data sends correct data onto the UART +TEST_F(MctProtocolTest, TestSends) { + SendAndReadUartOutbound(HCI_PACKET_TYPE_COMMAND, sample_data1, + fake_uart_[CH_CMD]); + SendAndReadUartOutbound(HCI_PACKET_TYPE_ACL_DATA, sample_data2, + fake_uart_[CH_ACL_OUT]); +} + +// Ensure we properly parse data coming from the UART +TEST_F(MctProtocolTest, TestReads) { + WriteAndExpectInboundAclData(acl_data); + WriteAndExpectInboundEvent(event_data); +} + +} // namespace implementation +} // namespace V1_0 +} // namespace bluetooth +} // namespace hardware +} // namespace android diff --git a/bluetooth/1.0/default/vendor_interface.cc b/bluetooth/1.0/default/vendor_interface.cc index 2c55d1ce1b..6d3b56fd34 100644 --- a/bluetooth/1.0/default/vendor_interface.cc +++ b/bluetooth/1.0/default/vendor_interface.cc @@ -27,6 +27,8 @@ #include #include "bluetooth_address.h" +#include "h4_protocol.h" +#include "mct_protocol.h" static const char* VENDOR_LIBRARY_NAME = "libbt-vendor.so"; static const char* VENDOR_LIBRARY_SYMBOL_NAME = @@ -64,30 +66,6 @@ HC_BT_HDR* WrapPacketAndCopy(uint16_t event, const hidl_vec& data) { return packet; } -size_t write_safely(int fd, const uint8_t* data, size_t length) { - size_t transmitted_length = 0; - while (length > 0) { - ssize_t ret = - TEMP_FAILURE_RETRY(write(fd, data + transmitted_length, length)); - - if (ret == -1) { - if (errno == EAGAIN) continue; - ALOGE("%s error writing to UART (%s)", __func__, strerror(errno)); - break; - - } else if (ret == 0) { - // Nothing written :( - ALOGE("%s zero bytes written - something went wrong...", __func__); - break; - } - - transmitted_length += ret; - length -= ret; - } - - return transmitted_length; -} - bool internal_command_event_match(const hidl_vec& packet) { uint8_t event_code = packet[0]; if (event_code != HCI_COMMAND_COMPLETE_EVENT) { @@ -185,10 +163,12 @@ class FirmwareStartupTimer { bool VendorInterface::Initialize( InitializeCompleteCallback initialize_complete_cb, - PacketReadCallback packet_read_cb) { + PacketReadCallback event_cb, PacketReadCallback acl_cb, + PacketReadCallback sco_cb) { assert(!g_vendor_interface); g_vendor_interface = new VendorInterface(); - return g_vendor_interface->Open(initialize_complete_cb, packet_read_cb); + return g_vendor_interface->Open(initialize_complete_cb, event_cb, acl_cb, + sco_cb); } void VendorInterface::Shutdown() { @@ -201,9 +181,10 @@ void VendorInterface::Shutdown() { VendorInterface* VendorInterface::get() { return g_vendor_interface; } bool VendorInterface::Open(InitializeCompleteCallback initialize_complete_cb, - PacketReadCallback packet_read_cb) { + PacketReadCallback event_cb, + PacketReadCallback acl_cb, + PacketReadCallback sco_cb) { initialize_complete_cb_ = initialize_complete_cb; - packet_read_cb_ = packet_read_cb; // Initialize vendor interface @@ -241,28 +222,41 @@ bool VendorInterface::Open(InitializeCompleteCallback initialize_complete_cb, power_state = BT_VND_PWR_ON; lib_interface_->op(BT_VND_OP_POWER_CTRL, &power_state); - // Get the UART socket + // Get the UART socket(s) int fd_list[CH_MAX] = {0}; int fd_count = lib_interface_->op(BT_VND_OP_USERIAL_OPEN, &fd_list); - if (fd_count != 1) { - ALOGE("%s fd count %d != 1; we can't handle this currently...", __func__, - fd_count); - return false; + for (int i = 0; i < fd_count; i++) { + if (fd_list[i] == INVALID_FD) { + ALOGE("%s: fd %d is invalid!", __func__, fd_list[i]); + return false; + } } - uart_fd_ = fd_list[0]; - if (uart_fd_ == INVALID_FD) { - ALOGE("%s unable to determine UART fd", __func__); - return false; + event_cb_ = event_cb; + PacketReadCallback intercept_events = [this](const hidl_vec& event) { + HandleIncomingEvent(event); + }; + + if (fd_count == 1) { + hci::H4Protocol* h4_hci = + new hci::H4Protocol(fd_list[0], intercept_events, acl_cb, sco_cb); + fd_watcher_.WatchFdForNonBlockingReads( + fd_list[0], [h4_hci](int fd) { h4_hci->OnDataReady(fd); }); + hci_ = h4_hci; + } else { + hci::MctProtocol* mct_hci = + new hci::MctProtocol(fd_list, intercept_events, acl_cb); + fd_watcher_.WatchFdForNonBlockingReads( + fd_list[CH_EVT], [mct_hci](int fd) { mct_hci->OnEventDataReady(fd); }); + if (fd_count >= CH_ACL_IN) + fd_watcher_.WatchFdForNonBlockingReads( + fd_list[CH_ACL_IN], + [mct_hci](int fd) { mct_hci->OnAclDataReady(fd); }); + hci_ = mct_hci; } - ALOGI("%s UART fd: %d", __func__, uart_fd_); - - fd_watcher_.WatchFdForNonBlockingReads(uart_fd_, - [this](int fd) { hci_packetizer_.OnDataReady(fd); }); - // Initially, the power management is off. lpm_wake_deasserted = true; @@ -276,12 +270,16 @@ bool VendorInterface::Open(InitializeCompleteCallback initialize_complete_cb, void VendorInterface::Close() { fd_watcher_.StopWatchingFileDescriptors(); + if (hci_ != nullptr) { + delete hci_; + hci_ = nullptr; + } + if (lib_interface_ != nullptr) { bt_vendor_lpm_mode_t mode = BT_VND_LPM_DISABLE; lib_interface_->op(BT_VND_OP_LPM_SET_MODE, &mode); lib_interface_->op(BT_VND_OP_USERIAL_CLOSE, nullptr); - uart_fd_ = INVALID_FD; int power_state = BT_VND_PWR_OFF; lib_interface_->op(BT_VND_OP_POWER_CTRL, &power_state); } @@ -298,8 +296,6 @@ void VendorInterface::Close() { } size_t VendorInterface::Send(uint8_t type, const uint8_t* data, size_t length) { - if (uart_fd_ == INVALID_FD) return 0; - recent_activity_flag = true; if (lpm_wake_deasserted == true) { @@ -313,11 +309,7 @@ size_t VendorInterface::Send(uint8_t type, const uint8_t* data, size_t length) { ALOGV("%s: Sent wake before (%02x)", __func__, data[0] | (data[1] << 8)); } - int rv = write_safely(uart_fd_, &type, sizeof(type)); - if (rv == sizeof(type)) - rv = write_safely(uart_fd_, data, length); - - return rv; + return hci_->Send(type, data, length); } void VendorInterface::OnFirmwareConfigured(uint8_t result) { @@ -357,26 +349,18 @@ void VendorInterface::OnTimeout() { recent_activity_flag = false; } -void VendorInterface::OnPacketReady() { - VendorInterface::get()->HandleIncomingPacket(); -} +void VendorInterface::HandleIncomingEvent(const hidl_vec& hci_packet) { + if (internal_command.cb != nullptr && + internal_command_event_match(hci_packet)) { + HC_BT_HDR* bt_hdr = WrapPacketAndCopy(HCI_PACKET_TYPE_EVENT, hci_packet); -void VendorInterface::HandleIncomingPacket() { - HciPacketType hci_packet_type = hci_packetizer_.GetPacketType(); - hidl_vec hci_packet = hci_packetizer_.GetPacket(); - if (internal_command.cb != nullptr && - hci_packet_type == HCI_PACKET_TYPE_EVENT && - internal_command_event_match(hci_packet)) { - HC_BT_HDR* bt_hdr = - WrapPacketAndCopy(HCI_PACKET_TYPE_EVENT, hci_packet); - - // The callbacks can send new commands, so don't zero after calling. - tINT_CMD_CBACK saved_cb = internal_command.cb; - internal_command.cb = nullptr; - saved_cb(bt_hdr); - } else { - packet_read_cb_(hci_packet_type, hci_packet); - } + // The callbacks can send new commands, so don't zero after calling. + tINT_CMD_CBACK saved_cb = internal_command.cb; + internal_command.cb = nullptr; + saved_cb(bt_hdr); + } else { + event_cb_(hci_packet); + } } } // namespace implementation diff --git a/bluetooth/1.0/default/vendor_interface.h b/bluetooth/1.0/default/vendor_interface.h index 81156405a0..a401ee6e55 100644 --- a/bluetooth/1.0/default/vendor_interface.h +++ b/bluetooth/1.0/default/vendor_interface.h @@ -20,7 +20,7 @@ #include "async_fd_watcher.h" #include "bt_vendor_lib.h" -#include "hci_packetizer.h" +#include "hci_protocol.h" namespace android { namespace hardware { @@ -30,15 +30,15 @@ namespace implementation { using ::android::hardware::hidl_vec; using InitializeCompleteCallback = std::function; -using PacketReadCallback = - std::function &)>; +using PacketReadCallback = std::function&)>; class FirmwareStartupTimer; class VendorInterface { public: static bool Initialize(InitializeCompleteCallback initialize_complete_cb, - PacketReadCallback packet_read_cb); + PacketReadCallback event_cb, PacketReadCallback acl_cb, + PacketReadCallback sco_cb); static void Shutdown(); static VendorInterface *get(); @@ -46,27 +46,25 @@ class VendorInterface { void OnFirmwareConfigured(uint8_t result); - static void OnPacketReady(); - private: virtual ~VendorInterface() = default; bool Open(InitializeCompleteCallback initialize_complete_cb, - PacketReadCallback packet_read_cb); + PacketReadCallback event_cb, PacketReadCallback acl_cb, + PacketReadCallback sco_cb); void Close(); void OnTimeout(); - void HandleIncomingPacket(); + void HandleIncomingEvent(const hidl_vec& hci_packet); void *lib_handle_; bt_vendor_interface_t *lib_interface_; async::AsyncFdWatcher fd_watcher_; - int uart_fd_; - PacketReadCallback packet_read_cb_; InitializeCompleteCallback initialize_complete_cb_; + hci::HciProtocol* hci_; - hci::HciPacketizer hci_packetizer_ {VendorInterface::OnPacketReady}; + PacketReadCallback event_cb_; FirmwareStartupTimer *firmware_startup_timer_; };