diff options
author | Myles Watson <mylesgw@google.com> | 2017-03-03 13:50:49 -0800 |
---|---|---|
committer | Myles Watson <mylesgw@google.com> | 2017-03-07 16:57:16 -0800 |
commit | 761dc49c591d9fe6cc1a3a2e90026678af3ad41a (patch) | |
tree | a9ea82b83f9946fb5d1020e2704adf7cf9222bfb | |
parent | 34e8b2302597520b619ecc59162cfdf781f16665 (diff) | |
download | hikey-761dc49c591d9fe6cc1a3a2e90026678af3ad41a.tar.gz |
hikey: Make the Hikey Bluetooth HAL independentlinaro-optee
Test: Bluetooth starts/stops
Change-Id: I8a8b113f3edcdb092c36fd7822aeabf900c0f0b3
-rw-r--r-- | bluetooth/Android.bp | 9 | ||||
-rw-r--r-- | bluetooth/async_fd_watcher.cc | 181 | ||||
-rw-r--r-- | bluetooth/async_fd_watcher.h | 66 | ||||
-rw-r--r-- | bluetooth/bluetooth_hci.cc | 144 | ||||
-rw-r--r-- | bluetooth/bluetooth_hci.h | 8 | ||||
-rw-r--r-- | bluetooth/h4_protocol.cc | 71 | ||||
-rw-r--r-- | bluetooth/h4_protocol.h | 60 | ||||
-rw-r--r-- | bluetooth/hci_internals.h | 49 | ||||
-rw-r--r-- | bluetooth/hci_packetizer.cc | 91 | ||||
-rw-r--r-- | bluetooth/hci_packetizer.h | 53 | ||||
-rw-r--r-- | bluetooth/hci_packetizer_hikey.cc | 121 | ||||
-rw-r--r-- | bluetooth/hci_protocol.cc | 74 | ||||
-rw-r--r-- | bluetooth/hci_protocol.h (renamed from bluetooth/hci_packetizer_hikey.h) | 26 |
13 files changed, 724 insertions, 229 deletions
diff --git a/bluetooth/Android.bp b/bluetooth/Android.bp index aecdcb7e..142ced28 100644 --- a/bluetooth/Android.bp +++ b/bluetooth/Android.bp @@ -18,8 +18,11 @@ cc_binary { 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 @@ cc_binary { "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 00000000..c4470d06 --- /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 00000000..b51f9211 --- /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 64107656..fa14b539 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 bluetooth { 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 91fad272..484a6ab4 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 @@ class BluetoothHci : public IBluetoothHci { 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 00000000..8f24b5ee --- /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 00000000..67e2b03e --- /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 00000000..1e1f3001 --- /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 00000000..9549858b --- /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 00000000..90579bd2 --- /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 9b54b8e0..00000000 --- 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_protocol.cc b/bluetooth/hci_protocol.cc new file mode 100644 index 00000000..cd709b43 --- /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_packetizer_hikey.h b/bluetooth/hci_protocol.h index 12fdde28..f76cddf5 100644 --- a/bluetooth/hci_packetizer_hikey.h +++ b/bluetooth/hci_protocol.h @@ -16,23 +16,33 @@ #pragma once +#include <hidl/HidlSupport.h> + +#include "hci_internals.h" #include "hci_packetizer.h" namespace android { namespace hardware { namespace bluetooth { -namespace V1_0 { -namespace hikey { +namespace hci { + +using ::android::hardware::hidl_vec; +using PacketReadCallback = std::function<void(const hidl_vec<uint8_t>&)>; -class HciPacketizerHikey : public hci::HciPacketizer { +// Implementation of HCI protocol bits common to different transports +class HciProtocol { public: - HciPacketizerHikey(hci::HciPacketReadyCallback packet_cb) - : HciPacketizer(packet_cb){}; - void OnDataReadyHikey(int fd); + 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 hikey -} // namespace V1_0 +} // namespace hci } // namespace bluetooth } // namespace hardware } // namespace android |