| // |
| // Copyright 2015 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 "hci_transport" |
| |
| #include <cinttypes> |
| |
| #include "vendor_libs/test_vendor_lib/include/hci_transport.h" |
| |
| #include "base/bind.h" |
| #include "base/logging.h" |
| #include "base/thread_task_runner_handle.h" |
| |
| extern "C" { |
| #include <sys/socket.h> |
| |
| #include "osi/include/log.h" |
| #include "stack/include/hcidefs.h" |
| } // extern "C" |
| |
| namespace test_vendor_lib { |
| |
| HciTransport::HciTransport() : weak_ptr_factory_(this) {} |
| |
| void HciTransport::CloseHciFd() { |
| hci_fd_.reset(nullptr); |
| } |
| |
| void HciTransport::CloseVendorFd() { |
| vendor_fd_.reset(nullptr); |
| } |
| |
| int HciTransport::GetHciFd() const { |
| return hci_fd_->get(); |
| } |
| |
| int HciTransport::GetVendorFd() const { |
| return vendor_fd_->get(); |
| } |
| |
| bool HciTransport::SetUp() { |
| int socketpair_fds[2]; |
| // TODO(dennischeng): Use SOCK_SEQPACKET here. |
| const int success = socketpair(AF_LOCAL, SOCK_STREAM, 0, socketpair_fds); |
| if (success < 0) |
| return false; |
| hci_fd_.reset(new base::ScopedFD(socketpair_fds[0])); |
| vendor_fd_.reset(new base::ScopedFD(socketpair_fds[1])); |
| return true; |
| } |
| |
| void HciTransport::OnFileCanReadWithoutBlocking(int fd) { |
| CHECK(fd == GetVendorFd()); |
| LOG_INFO(LOG_TAG, "Event ready in HciTransport on fd: %d.", fd); |
| |
| const serial_data_type_t packet_type = packet_stream_.ReceivePacketType(fd); |
| switch (packet_type) { |
| case (DATA_TYPE_COMMAND): { |
| ReceiveReadyCommand(); |
| break; |
| } |
| |
| case (DATA_TYPE_ACL): { |
| LOG_INFO(LOG_TAG, "ACL data packets not currently supported."); |
| break; |
| } |
| |
| case (DATA_TYPE_SCO): { |
| LOG_INFO(LOG_TAG, "SCO data packets not currently supported."); |
| break; |
| } |
| |
| // TODO(dennischeng): Add debug level assert here. |
| default: { |
| LOG_INFO(LOG_TAG, "Error received an invalid packet type from the HCI."); |
| break; |
| } |
| } |
| } |
| |
| void HciTransport::ReceiveReadyCommand() const { |
| std::unique_ptr<CommandPacket> command = |
| packet_stream_.ReceiveCommand(GetVendorFd()); |
| LOG_INFO(LOG_TAG, "Received command packet."); |
| command_handler_(std::move(command)); |
| } |
| |
| void HciTransport::RegisterCommandHandler( |
| std::function<void(std::unique_ptr<CommandPacket>)> callback) { |
| command_handler_ = callback; |
| } |
| |
| void HciTransport::OnFileCanWriteWithoutBlocking(int fd) { |
| CHECK(fd == GetVendorFd()); |
| if (!outbound_events_.empty()) { |
| base::TimeTicks current_time = base::TimeTicks::Now(); |
| // Check outbound events for events that can be sent, i.e. events with a |
| // timestamp before the current time. Stop sending events when |
| // |packet_stream_| fails writing. |
| for (auto it = outbound_events_.begin(); it != outbound_events_.end();) { |
| if ((*it)->GetTimeStamp() > current_time) { |
| ++it; |
| continue; |
| } |
| if (!packet_stream_.SendEvent((*it)->GetEvent(), fd)) |
| return; |
| it = outbound_events_.erase(it); |
| } |
| } |
| } |
| |
| void HciTransport::AddEventToOutboundEvents( |
| std::unique_ptr<TimeStampedEvent> event) { |
| outbound_events_.push_back(std::move(event)); |
| } |
| |
| void HciTransport::PostEventResponse(std::unique_ptr<EventPacket> event) { |
| AddEventToOutboundEvents( |
| std::make_unique<TimeStampedEvent>(std::move(event))); |
| } |
| |
| void HciTransport::PostDelayedEventResponse(std::unique_ptr<EventPacket> event, |
| base::TimeDelta delay) { |
| // TODO(dennischeng): When it becomes available for MessageLoopForIO, use the |
| // thread's task runner to post |PostEventResponse| as a delayed task, being |
| // sure to CHECK the appropriate task runner attributes using |
| // base::ThreadTaskRunnerHandle. |
| |
| // The system does not support high resolution timing and the clock could be |
| // as coarse as ~15.6 ms so the event is sent without a delay to avoid |
| // inconsistent event responses. |
| if (!base::TimeTicks::IsHighResolution()) { |
| LOG_INFO(LOG_TAG, |
| "System does not support high resolution timing. Sending event " |
| "without delay."); |
| PostEventResponse(std::move(event)); |
| } |
| |
| LOG_INFO(LOG_TAG, |
| "Posting event response with delay of %" PRId64 " ms.", |
| delay.InMilliseconds()); |
| |
| AddEventToOutboundEvents( |
| std::make_unique<TimeStampedEvent>(std::move(event), delay)); |
| } |
| |
| HciTransport::TimeStampedEvent::TimeStampedEvent( |
| std::unique_ptr<EventPacket> event, base::TimeDelta delay) |
| : event_(std::move(event)), time_stamp_(base::TimeTicks::Now() + delay) {} |
| |
| HciTransport::TimeStampedEvent::TimeStampedEvent( |
| std::unique_ptr<EventPacket> event) |
| : event_(std::move(event)), time_stamp_(base::TimeTicks::UnixEpoch()) {} |
| |
| const base::TimeTicks& HciTransport::TimeStampedEvent::GetTimeStamp() const { |
| return time_stamp_; |
| } |
| |
| const EventPacket& HciTransport::TimeStampedEvent::GetEvent() { |
| return *(event_.get()); |
| } |
| |
| } // namespace test_vendor_lib |