hikey: Make the Hikey Bluetooth HAL independent
Test: Bluetooth starts/stops
Change-Id: I8a8b113f3edcdb092c36fd7822aeabf900c0f0b3
diff --git a/bluetooth/Android.bp b/bluetooth/Android.bp
index aecdcb7..142ced2 100644
--- a/bluetooth/Android.bp
+++ b/bluetooth/Android.bp
@@ -18,8 +18,11 @@
proprietary: true,
relative_install_path: "hw",
srcs: [
+ "async_fd_watcher.cc",
"bluetooth_hci.cc",
- "hci_packetizer_hikey.cc",
+ "h4_protocol.cc",
+ "hci_packetizer.cc",
+ "hci_protocol.cc",
"service.cc",
],
shared_libs: [
@@ -33,9 +36,5 @@
"liblog",
"libutils",
],
- static_libs: [
- "android.hardware.bluetooth-async",
- "android.hardware.bluetooth-hci",
- ],
init_rc: ["android.hardware.bluetooth@1.0-service.hikey.rc"],
}
diff --git a/bluetooth/async_fd_watcher.cc b/bluetooth/async_fd_watcher.cc
new file mode 100644
index 0000000..c4470d0
--- /dev/null
+++ b/bluetooth/async_fd_watcher.cc
@@ -0,0 +1,181 @@
+//
+// Copyright 2016 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 "async_fd_watcher.h"
+
+#include <algorithm>
+#include <atomic>
+#include <condition_variable>
+#include <map>
+#include <mutex>
+#include <thread>
+#include <vector>
+#include "fcntl.h"
+#include "sys/select.h"
+#include "unistd.h"
+
+static const int INVALID_FD = -1;
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace async {
+
+int AsyncFdWatcher::WatchFdForNonBlockingReads(
+ int file_descriptor, const ReadCallback& on_read_fd_ready_callback) {
+ // Add file descriptor and callback
+ {
+ std::unique_lock<std::mutex> guard(internal_mutex_);
+ watched_fds_[file_descriptor] = on_read_fd_ready_callback;
+ }
+
+ // Start the thread if not started yet
+ return tryStartThread();
+}
+
+int AsyncFdWatcher::ConfigureTimeout(
+ const std::chrono::milliseconds timeout,
+ const TimeoutCallback& on_timeout_callback) {
+ // Add timeout and callback
+ {
+ std::unique_lock<std::mutex> guard(timeout_mutex_);
+ timeout_cb_ = on_timeout_callback;
+ timeout_ms_ = timeout;
+ }
+
+ notifyThread();
+ return 0;
+}
+
+void AsyncFdWatcher::StopWatchingFileDescriptors() { stopThread(); }
+
+AsyncFdWatcher::~AsyncFdWatcher() {}
+
+// Make sure to call this with at least one file descriptor ready to be
+// watched upon or the thread routine will return immediately
+int AsyncFdWatcher::tryStartThread() {
+ if (std::atomic_exchange(&running_, true)) return 0;
+
+ // Set up the communication channel
+ int pipe_fds[2];
+ if (pipe2(pipe_fds, O_NONBLOCK)) return -1;
+
+ notification_listen_fd_ = pipe_fds[0];
+ notification_write_fd_ = pipe_fds[1];
+
+ thread_ = std::thread([this]() { ThreadRoutine(); });
+ if (!thread_.joinable()) return -1;
+
+ return 0;
+}
+
+int AsyncFdWatcher::stopThread() {
+ if (!std::atomic_exchange(&running_, false)) return 0;
+
+ notifyThread();
+ if (std::this_thread::get_id() != thread_.get_id()) {
+ thread_.join();
+ }
+
+ {
+ std::unique_lock<std::mutex> guard(internal_mutex_);
+ watched_fds_.clear();
+ }
+
+ {
+ std::unique_lock<std::mutex> guard(timeout_mutex_);
+ timeout_cb_ = nullptr;
+ }
+
+ return 0;
+}
+
+int AsyncFdWatcher::notifyThread() {
+ uint8_t buffer[] = {0};
+ if (TEMP_FAILURE_RETRY(write(notification_write_fd_, &buffer, 1)) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+void AsyncFdWatcher::ThreadRoutine() {
+ while (running_) {
+ fd_set read_fds;
+ FD_ZERO(&read_fds);
+ FD_SET(notification_listen_fd_, &read_fds);
+ int max_read_fd = INVALID_FD;
+ for (auto& it : watched_fds_) {
+ FD_SET(it.first, &read_fds);
+ max_read_fd = std::max(max_read_fd, it.first);
+ }
+
+ struct timeval timeout;
+ struct timeval* timeout_ptr = NULL;
+ if (timeout_ms_ > std::chrono::milliseconds(0)) {
+ timeout.tv_sec = timeout_ms_.count() / 1000;
+ timeout.tv_usec = (timeout_ms_.count() % 1000) * 1000;
+ timeout_ptr = &timeout;
+ }
+
+ // Wait until there is data available to read on some FD.
+ int nfds = std::max(notification_listen_fd_, max_read_fd);
+ int retval = select(nfds + 1, &read_fds, NULL, NULL, timeout_ptr);
+
+ // There was some error.
+ if (retval < 0) continue;
+
+ // Timeout.
+ if (retval == 0) {
+ // Allow the timeout callback to modify the timeout.
+ TimeoutCallback saved_cb;
+ {
+ std::unique_lock<std::mutex> guard(timeout_mutex_);
+ if (timeout_ms_ > std::chrono::milliseconds(0)) saved_cb = timeout_cb_;
+ }
+ if (saved_cb != nullptr) saved_cb();
+ continue;
+ }
+
+ // Read data from the notification FD.
+ if (FD_ISSET(notification_listen_fd_, &read_fds)) {
+ char buffer[] = {0};
+ TEMP_FAILURE_RETRY(read(notification_listen_fd_, buffer, 1));
+ continue;
+ }
+
+ // Invoke the data ready callbacks if appropriate.
+ std::vector<decltype(watched_fds_)::value_type> saved_callbacks;
+ {
+ std::unique_lock<std::mutex> guard(internal_mutex_);
+ for (auto& it : watched_fds_) {
+ if (FD_ISSET(it.first, &read_fds)) {
+ saved_callbacks.push_back(it);
+ }
+ }
+ }
+
+ for (auto& it : saved_callbacks) {
+ if (it.second) {
+ it.second(it.first);
+ }
+ }
+ }
+}
+
+} // namespace async
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/async_fd_watcher.h b/bluetooth/async_fd_watcher.h
new file mode 100644
index 0000000..b51f921
--- /dev/null
+++ b/bluetooth/async_fd_watcher.h
@@ -0,0 +1,66 @@
+//
+// Copyright 2016 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 <map>
+#include <mutex>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace async {
+
+using ReadCallback = std::function<void(int)>;
+using TimeoutCallback = std::function<void(void)>;
+
+class AsyncFdWatcher {
+ public:
+ AsyncFdWatcher() = default;
+ ~AsyncFdWatcher();
+
+ int WatchFdForNonBlockingReads(int file_descriptor,
+ const ReadCallback& on_read_fd_ready_callback);
+ int ConfigureTimeout(const std::chrono::milliseconds timeout,
+ const TimeoutCallback& on_timeout_callback);
+ void StopWatchingFileDescriptors();
+
+ private:
+ AsyncFdWatcher(const AsyncFdWatcher&) = delete;
+ AsyncFdWatcher& operator=(const AsyncFdWatcher&) = delete;
+
+ int tryStartThread();
+ int stopThread();
+ int notifyThread();
+ void ThreadRoutine();
+
+ std::atomic_bool running_{false};
+ std::thread thread_;
+ std::mutex internal_mutex_;
+ std::mutex timeout_mutex_;
+
+ std::map<int, ReadCallback> watched_fds_;
+ int notification_listen_fd_;
+ int notification_write_fd_;
+ TimeoutCallback timeout_cb_;
+ std::chrono::milliseconds timeout_ms_;
+};
+
+} // namespace async
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/bluetooth_hci.cc b/bluetooth/bluetooth_hci.cc
index 6410765..fa14b53 100644
--- a/bluetooth/bluetooth_hci.cc
+++ b/bluetooth/bluetooth_hci.cc
@@ -18,44 +18,11 @@
#include "bluetooth_hci.h"
-#include <utils/Log.h>
-
#include <android-base/logging.h>
-
-#include "hci_internals.h"
-
-namespace {
-
-using android::hardware::bluetooth::V1_0::hikey::BluetoothHci;
-using android::hardware::hidl_vec;
-
-BluetoothHci* g_bluetooth_hci = nullptr;
-
-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;
-}
-
-} // namespace
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <utils/Log.h>
namespace android {
namespace hardware {
@@ -63,88 +30,87 @@
namespace V1_0 {
namespace hikey {
+using android::hardware::hidl_vec;
+
Return<void> BluetoothHci::initialize(
const ::android::sp<IBluetoothHciCallbacks>& cb) {
ALOGI("BluetoothHci::initialize()");
- CHECK(cb != nullptr);
- event_cb_ = cb;
-
hci_tty_fd_ = open("/dev/hci_tty", O_RDWR);
if (hci_tty_fd_ < 0) {
- ALOGE("%s: Can't open hci_tty", __func__);
- event_cb_->initializationComplete(Status::INITIALIZATION_ERROR);
+ ALOGE("%s: Can't open hci_tty (%s)", __func__, strerror(errno));
+ cb->initializationComplete(Status::INITIALIZATION_ERROR);
+ return Void();
}
- CHECK(g_bluetooth_hci == nullptr) << __func__ << " is not reentrant";
- g_bluetooth_hci = this;
+ event_cb_ = cb;
+
+ hci_ = new hci::H4Protocol(
+ hci_tty_fd_,
+ [cb](const hidl_vec<uint8_t>& packet) { cb->hciEventReceived(packet); },
+ [cb](const hidl_vec<uint8_t>& packet) { cb->aclDataReceived(packet); },
+ [cb](const hidl_vec<uint8_t>& packet) { cb->scoDataReceived(packet); });
+
+ // Use a socket pair to enforce the TI FIONREAD requirement.
+ int sockfd[2];
+ socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd);
+ int shim_fd = sockfd[0];
+ int for_hci = sockfd[1];
+
+ fd_watcher_.WatchFdForNonBlockingReads(hci_tty_fd_, [this, shim_fd](int fd) {
+ int tty_bytes = 0;
+ if (TEMP_FAILURE_RETRY(ioctl(fd, FIONREAD, &tty_bytes)))
+ ALOGE("%s:FIONREAD %s", __func__, strerror(errno));
+ ALOGV("%s:tty_bytes = %d", __func__, tty_bytes);
+
+ uint8_t* tmp_buffer = new uint8_t[tty_bytes];
+ size_t bytes_read = TEMP_FAILURE_RETRY(read(fd, tmp_buffer, tty_bytes));
+ CHECK(static_cast<int>(bytes_read) == tty_bytes);
+ size_t bytes_written =
+ TEMP_FAILURE_RETRY(write(shim_fd, tmp_buffer, tty_bytes));
+ CHECK(static_cast<int>(bytes_written) == tty_bytes);
+ delete[] tmp_buffer;
+ });
fd_watcher_.WatchFdForNonBlockingReads(
- hci_tty_fd_, [this](int fd) { hci_packetizer_.OnDataReadyHikey(fd); });
+ for_hci, [this](int fd) { hci_->OnDataReady(fd); });
- event_cb_->initializationComplete(Status::SUCCESS);
+ cb->initializationComplete(Status::SUCCESS);
return Void();
}
Return<void> BluetoothHci::close() {
- ALOGW("BluetoothHci::close()");
- ::close(hci_tty_fd_);
- hci_tty_fd_ = -1;
- g_bluetooth_hci = nullptr;
+ ALOGI("BluetoothHci::close()");
+
+ if (hci_tty_fd_ >= 0) {
+ fd_watcher_.StopWatchingFileDescriptors();
+ ::close(hci_tty_fd_);
+ hci_tty_fd_ = -1;
+ }
+
+ if (hci_ != nullptr) {
+ delete hci_;
+ hci_ = nullptr;
+ }
+
return Void();
}
Return<void> BluetoothHci::sendHciCommand(const hidl_vec<uint8_t>& packet) {
- uint8_t type = HCI_PACKET_TYPE_COMMAND;
- int rv = write_safely(hci_tty_fd_, &type, sizeof(type));
- if (rv == sizeof(type))
- rv = write_safely(hci_tty_fd_, packet.data(), packet.size());
+ hci_->Send(HCI_PACKET_TYPE_COMMAND, packet.data(), packet.size());
return Void();
}
Return<void> BluetoothHci::sendAclData(const hidl_vec<uint8_t>& packet) {
- uint8_t type = HCI_PACKET_TYPE_ACL_DATA;
- int rv = write_safely(hci_tty_fd_, &type, sizeof(type));
- if (rv == sizeof(type))
- rv = write_safely(hci_tty_fd_, packet.data(), packet.size());
+ hci_->Send(HCI_PACKET_TYPE_ACL_DATA, packet.data(), packet.size());
return Void();
}
Return<void> BluetoothHci::sendScoData(const hidl_vec<uint8_t>& packet) {
- uint8_t type = HCI_PACKET_TYPE_SCO_DATA;
- int rv = write_safely(hci_tty_fd_, &type, sizeof(type));
- if (rv == sizeof(type))
- rv = write_safely(hci_tty_fd_, packet.data(), packet.size());
+ hci_->Send(HCI_PACKET_TYPE_SCO_DATA, packet.data(), packet.size());
return Void();
}
-BluetoothHci* BluetoothHci::get() { return g_bluetooth_hci; }
-
-void BluetoothHci::OnPacketReady() {
- BluetoothHci::get()->HandleIncomingPacket();
-}
-
-void BluetoothHci::HandleIncomingPacket() {
- HciPacketType hci_packet_type = hci_packetizer_.GetPacketType();
- hidl_vec<uint8_t> hci_packet = hci_packetizer_.GetPacket();
-
- switch (hci_packet_type) {
- case HCI_PACKET_TYPE_EVENT:
- event_cb_->hciEventReceived(hci_packet);
- break;
- case HCI_PACKET_TYPE_ACL_DATA:
- event_cb_->aclDataReceived(hci_packet);
- break;
- case HCI_PACKET_TYPE_SCO_DATA:
- event_cb_->scoDataReceived(hci_packet);
- break;
- default: {
- bool hci_packet_type_corrupted = true;
- CHECK(hci_packet_type_corrupted == false);
- }
- }
-}
-
} // namespace hikey
} // namespace V1_0
} // namespace bluetooth
diff --git a/bluetooth/bluetooth_hci.h b/bluetooth/bluetooth_hci.h
index 91fad27..484a6ab 100644
--- a/bluetooth/bluetooth_hci.h
+++ b/bluetooth/bluetooth_hci.h
@@ -21,7 +21,7 @@
#include <hidl/MQDescriptor.h>
#include "async_fd_watcher.h"
-#include "hci_packetizer_hikey.h"
+#include "h4_protocol.h"
namespace android {
namespace hardware {
@@ -43,17 +43,13 @@
static void OnPacketReady();
- static BluetoothHci* get();
-
private:
::android::sp<IBluetoothHciCallbacks> event_cb_;
int hci_tty_fd_;
- void HandleIncomingPacket();
-
async::AsyncFdWatcher fd_watcher_;
- HciPacketizerHikey hci_packetizer_{BluetoothHci::OnPacketReady};
+ hci::H4Protocol* hci_;
};
} // namespace hikey
diff --git a/bluetooth/h4_protocol.cc b/bluetooth/h4_protocol.cc
new file mode 100644
index 0000000..8f24b5e
--- /dev/null
+++ b/bluetooth/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 <android-base/logging.h>
+#include <assert.h>
+#include <fcntl.h>
+
+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<HciPacketType>(buffer[0]);
+ } else {
+ hci_packetizer_.OnDataReady(fd, hci_packet_type_);
+ }
+}
+
+} // namespace hci
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/h4_protocol.h b/bluetooth/h4_protocol.h
new file mode 100644
index 0000000..67e2b03
--- /dev/null
+++ b/bluetooth/h4_protocol.h
@@ -0,0 +1,60 @@
+//
+// 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 <hidl/HidlSupport.h>
+
+#include "async_fd_watcher.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/hci_internals.h b/bluetooth/hci_internals.h
new file mode 100644
index 0000000..1e1f300
--- /dev/null
+++ b/bluetooth/hci_internals.h
@@ -0,0 +1,49 @@
+//
+// Copyright 2016 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 <stdlib.h>
+
+// HCI UART transport packet types (Volume 4, Part A, 2)
+enum HciPacketType {
+ HCI_PACKET_TYPE_UNKNOWN = 0,
+ HCI_PACKET_TYPE_COMMAND = 1,
+ HCI_PACKET_TYPE_ACL_DATA = 2,
+ HCI_PACKET_TYPE_SCO_DATA = 3,
+ HCI_PACKET_TYPE_EVENT = 4
+};
+
+// 2 bytes for opcode, 1 byte for parameter length (Volume 2, Part E, 5.4.1)
+const size_t HCI_COMMAND_PREAMBLE_SIZE = 3;
+const size_t HCI_LENGTH_OFFSET_CMD = 2;
+
+// 2 bytes for handle, 2 bytes for data length (Volume 2, Part E, 5.4.2)
+const size_t HCI_ACL_PREAMBLE_SIZE = 4;
+const size_t HCI_LENGTH_OFFSET_ACL = 2;
+
+// 2 bytes for handle, 1 byte for data length (Volume 2, Part E, 5.4.3)
+const size_t HCI_SCO_PREAMBLE_SIZE = 3;
+const size_t HCI_LENGTH_OFFSET_SCO = 2;
+
+// 1 byte for event code, 1 byte for parameter length (Volume 2, Part E, 5.4.4)
+const size_t HCI_EVENT_PREAMBLE_SIZE = 2;
+const size_t HCI_LENGTH_OFFSET_EVT = 1;
+
+const size_t HCI_PREAMBLE_SIZE_MAX = HCI_ACL_PREAMBLE_SIZE;
+
+// Event codes (Volume 2, Part E, 7.7.14)
+const uint8_t HCI_COMMAND_COMPLETE_EVENT = 0x0E;
diff --git a/bluetooth/hci_packetizer.cc b/bluetooth/hci_packetizer.cc
new file mode 100644
index 0000000..9549858
--- /dev/null
+++ b/bluetooth/hci_packetizer.cc
@@ -0,0 +1,91 @@
+//
+// 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_packetizer.h"
+
+#define LOG_TAG "android.hardware.bluetooth.hci_packetizer"
+#include <android-base/logging.h>
+#include <utils/Log.h>
+
+#include <dlfcn.h>
+#include <fcntl.h>
+
+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 {
+
+const hidl_vec<uint8_t>& HciPacketizer::GetPacket() const { return packet_; }
+
+void HciPacketizer::OnDataReady(int fd, HciPacketType packet_type) {
+ switch (state_) {
+ case HCI_PREAMBLE: {
+ size_t bytes_read = TEMP_FAILURE_RETRY(
+ read(fd, preamble_ + bytes_read_,
+ preamble_size_for_type[packet_type] - bytes_read_));
+ CHECK(bytes_read > 0);
+ bytes_read_ += bytes_read;
+ if (bytes_read_ == preamble_size_for_type[packet_type]) {
+ size_t packet_length =
+ 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,
+ packet_.data() + preamble_size_for_type[packet_type] + bytes_read_,
+ bytes_remaining_));
+ CHECK(bytes_read > 0);
+ bytes_remaining_ -= bytes_read;
+ bytes_read_ += bytes_read;
+ if (bytes_remaining_ == 0) {
+ packet_ready_cb_();
+ state_ = HCI_PREAMBLE;
+ bytes_read_ = 0;
+ }
+ break;
+ }
+ }
+}
+
+} // namespace hci
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/hci_packetizer.h b/bluetooth/hci_packetizer.h
new file mode 100644
index 0000000..90579bd
--- /dev/null
+++ b/bluetooth/hci_packetizer.h
@@ -0,0 +1,53 @@
+//
+// 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 <functional>
+
+#include <hidl/HidlSupport.h>
+
+#include "hci_internals.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace hci {
+
+using ::android::hardware::hidl_vec;
+using HciPacketReadyCallback = std::function<void(void)>;
+
+class HciPacketizer {
+ public:
+ HciPacketizer(HciPacketReadyCallback packet_cb)
+ : packet_ready_cb_(packet_cb){};
+ void OnDataReady(int fd, HciPacketType packet_type);
+ const hidl_vec<uint8_t>& GetPacket() const;
+
+ protected:
+ enum State { HCI_PREAMBLE, HCI_PAYLOAD };
+ State state_{HCI_PREAMBLE};
+ uint8_t preamble_[HCI_PREAMBLE_SIZE_MAX];
+ hidl_vec<uint8_t> packet_;
+ size_t bytes_remaining_{0};
+ size_t bytes_read_{0};
+ HciPacketReadyCallback packet_ready_cb_;
+};
+
+} // namespace hci
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/hci_packetizer_hikey.cc b/bluetooth/hci_packetizer_hikey.cc
deleted file mode 100644
index 9b54b8e..0000000
--- a/bluetooth/hci_packetizer_hikey.cc
+++ /dev/null
@@ -1,121 +0,0 @@
-//
-// 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_packetizer_hikey.h"
-
-#include <sys/ioctl.h>
-#include <utils/Log.h>
-
-#include <android-base/logging.h>
-
-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 V1_0 {
-namespace hikey {
-
-void HciPacketizerHikey::OnDataReadyHikey(int fd) {
- int tty_bytes = 0;
- if (ioctl(fd, FIONREAD, &tty_bytes) == -1)
- ALOGE("%s:FIONREAD %s", __func__, strerror(errno));
- ALOGV("%s:tty_bytes = %d", __func__, tty_bytes);
- uint8_t* tmp_buffer = new uint8_t[tty_bytes];
- size_t bytes_read = TEMP_FAILURE_RETRY(read(fd, tmp_buffer, tty_bytes));
- CHECK(static_cast<int>(bytes_read) == tty_bytes);
-
- size_t index = 0;
- while (index < bytes_read - 1) {
- switch (hci_parser_state_) {
- case HCI_IDLE: {
- hci_packet_type_ = static_cast<HciPacketType>(tmp_buffer[index++]);
- CHECK(hci_packet_type_ >= HCI_PACKET_TYPE_ACL_DATA &&
- hci_packet_type_ <= HCI_PACKET_TYPE_EVENT)
- << "hci_packet_type_ = "
- << static_cast<unsigned int>(hci_packet_type_);
- 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: {
- size_t bytes_to_copy = (bytes_read - index < hci_packet_bytes_remaining_
- ? bytes_read - index
- : hci_packet_bytes_remaining_);
- memcpy(hci_packet_preamble_ + hci_packet_bytes_read_,
- &tmp_buffer[index], bytes_to_copy);
- hci_packet_bytes_remaining_ -= bytes_to_copy;
- hci_packet_bytes_read_ += bytes_to_copy;
- index += bytes_to_copy;
- ALOGV("%s:TYPE index = %d", __func__, static_cast<int>(index));
- if (hci_packet_bytes_remaining_ == 0) {
- 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;
- }
- break;
- }
-
- case HCI_PAYLOAD: {
- size_t bytes_to_copy = (bytes_read - index < hci_packet_bytes_remaining_
- ? bytes_read - index
- : hci_packet_bytes_remaining_);
- memcpy(hci_packet_.data() + preamble_size_for_type[hci_packet_type_] +
- hci_packet_bytes_read_,
- &tmp_buffer[index], bytes_to_copy);
- hci_packet_bytes_remaining_ -= bytes_to_copy;
- hci_packet_bytes_read_ += bytes_to_copy;
- index += bytes_to_copy;
- ALOGV("%s:PAYLOAD index = %d", __func__, static_cast<int>(index));
- if (hci_packet_bytes_remaining_ == 0) {
- hci_packet_ready_cb_();
- hci_parser_state_ = HCI_IDLE;
- }
- break;
- }
- }
- }
- delete[] tmp_buffer;
-}
-
-} // namespace hikey
-} // namespace V1_0
-} // namespace bluetooth
-} // namespace hardware
-} // namespace android
diff --git a/bluetooth/hci_packetizer_hikey.h b/bluetooth/hci_packetizer_hikey.h
deleted file mode 100644
index 12fdde2..0000000
--- a/bluetooth/hci_packetizer_hikey.h
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-// 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 "hci_packetizer.h"
-
-namespace android {
-namespace hardware {
-namespace bluetooth {
-namespace V1_0 {
-namespace hikey {
-
-class HciPacketizerHikey : public hci::HciPacketizer {
- public:
- HciPacketizerHikey(hci::HciPacketReadyCallback packet_cb)
- : HciPacketizer(packet_cb){};
- void OnDataReadyHikey(int fd);
-};
-
-} // namespace hikey
-} // namespace V1_0
-} // namespace bluetooth
-} // namespace hardware
-} // namespace android
diff --git a/bluetooth/hci_protocol.cc b/bluetooth/hci_protocol.cc
new file mode 100644
index 0000000..cd709b4
--- /dev/null
+++ b/bluetooth/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 <android-base/logging.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <utils/Log.h>
+
+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/hci_protocol.h b/bluetooth/hci_protocol.h
new file mode 100644
index 0000000..f76cddf
--- /dev/null
+++ b/bluetooth/hci_protocol.h
@@ -0,0 +1,48 @@
+//
+// 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 <hidl/HidlSupport.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<void(const hidl_vec<uint8_t>&)>;
+
+// 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