blob: 39b51f0581b89e7f46e10914378c2819f79be3aa [file] [log] [blame]
//
// 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