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_; };