Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5fef9ba3 authored by Myles Watson's avatar Myles Watson
Browse files

test_vendor: Refactor controller timing



Use AsyncManager to implement a timer tick and an event queue.

Change-Id: Iec2a0ef06f17ecce0652ecc52f5ef732fc4af026
Signed-off-by: default avatarMyles Watson <mylesgw@google.com>
parent fc1685f4
Loading
Loading
Loading
Loading
+38 −9
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
#include <vector>
using std::vector;

#include "async_manager.h"
#include "base/json/json_value_converter.h"
#include "base/time/time.h"
#include "command_packet.h"
@@ -181,16 +182,22 @@ class DualModeController {
  void RegisterHandlersWithTestChannelTransport(
      TestChannelTransport& transport);

  // Set the callbacks for scheduling tasks.
  void RegisterTaskScheduler(
      std::function<AsyncTaskId(std::chrono::milliseconds, const TaskCallback&)>
          evtScheduler);

  void RegisterPeriodicTaskScheduler(
      std::function<AsyncTaskId(std::chrono::milliseconds,
                                std::chrono::milliseconds,
                                const TaskCallback&)> periodicEvtScheduler);

  void RegisterTaskCancel(std::function<void(AsyncTaskId)> cancel);

  // Sets the callback to be used for sending events back to the HCI.
  // TODO(dennischeng): Once PostDelayedTask works, get rid of this and only use
  // |RegisterDelayedEventChannel|.
  void RegisterEventChannel(
      const std::function<void(std::unique_ptr<EventPacket>)>& send_event);

  void RegisterDelayedEventChannel(
      const std::function<void(std::unique_ptr<EventPacket>,
                               std::chrono::milliseconds)>& send_event);

  // Controller commands. For error codes, see the Bluetooth Core Specification,
  // Version 4.2, Volume 2, Part D (page 370).

@@ -324,6 +331,8 @@ class DualModeController {
  // Bluetooth Core Specification Version 4.2 Volume 2 Part E 7.1.1
  void HciInquiry(const vector<uint8_t>& args);

  void InquiryTimeout();

  // OGF: 0x0001
  // OCF: 0x0002
  // Bluetooth Core Specification Version 4.2 Volume 2 Part E 7.1.2
@@ -443,6 +452,11 @@ class DualModeController {
  // Causes all future HCI commands to timeout.
  void TestChannelTimeoutAll(const vector<std::string>& args);

  void HandleTimerTick();
  void SetTimerPeriod(std::chrono::milliseconds new_period);
  void StartTimer();
  void StopTimer();

 private:
  // Current link layer state of the controller.
  enum State {
@@ -456,6 +470,10 @@ class DualModeController {
    kDelayedResponse,  // Event responses are sent after a delay.
  };

  // Set a timer for a future action
  void AddControllerEvent(std::chrono::milliseconds,
                          const TaskCallback& callback);

  // Creates a command complete event and sends it back to the HCI.
  void SendCommandComplete(const uint16_t command_opcode,
                           const vector<uint8_t>& return_parameters) const;
@@ -477,12 +495,19 @@ class DualModeController {

  void SetEventDelay(int64_t delay);

  // Callbacks to schedule tasks.
  std::function<AsyncTaskId(std::chrono::milliseconds, const TaskCallback&)>
      schedule_task_;
  std::function<AsyncTaskId(std::chrono::milliseconds,
                            std::chrono::milliseconds,
                            const TaskCallback&)>
      schedule_periodic_task_;

  std::function<void(AsyncTaskId)> cancel_task_;

  // Callback provided to send events from the controller back to the HCI.
  std::function<void(std::unique_ptr<EventPacket>)> send_event_;

  std::function<void(std::unique_ptr<EventPacket>, std::chrono::milliseconds)>
      send_delayed_event_;

  // Maintains the commands to be registered and used in the HciHandler object.
  // Keys are command opcodes and values are the callbacks to handle each
  // command.
@@ -520,6 +545,10 @@ class DualModeController {

  TestChannelState test_channel_state_;

  std::vector<AsyncTaskId> controller_events_;
  AsyncTaskId timer_tick_task_;
  std::chrono::milliseconds timer_period_ = std::chrono::milliseconds(1000);

  DualModeController(const DualModeController& cmdPckt) = delete;
  DualModeController& operator=(const DualModeController& cmdPckt) = delete;
};
+5 −39
Original line number Diff line number Diff line
@@ -20,13 +20,7 @@
#include <list>
#include <memory>

extern "C" {
#include <sys/epoll.h>
}  // extern "C"

#include "async_manager.h"
#include "base/files/scoped_file.h"
#include "base/time/time.h"
#include "command_packet.h"
#include "event_packet.h"
#include "packet.h"
@@ -35,7 +29,7 @@ extern "C" {
namespace test_vendor_lib {

// Manages communication channel between HCI and the controller by providing the
// socketing mechanisms for reading/writing between the HCI and the controller.
// socket mechanisms for sending HCI [commands|events] [to|from] the controller.
class HciTransport {
 public:
  HciTransport();
@@ -59,45 +53,17 @@ class HciTransport {
  void RegisterCommandHandler(
      const std::function<void(std::unique_ptr<CommandPacket>)>& callback);

  // Sets the callback that is to schedule events.
  void RegisterEventScheduler(
      const std::function<void(std::chrono::milliseconds, const TaskCallback&)>&
          evtScheduler);

  // Sets the callback that is to schedule events.
  void RegisterPeriodicEventScheduler(
      const std::function<void(std::chrono::milliseconds,
                               std::chrono::milliseconds,
                               const TaskCallback&)>& periodicEvtScheduler);

  // Posts the event onto |outbound_events_| to be written sometime in the
  // future when the vendor file descriptor is ready for writing.
  void PostEventResponse(const EventPacket& event);
  // Blocks while it tries to writes the event to the vendor file descriptor.
  void PostEvent(const EventPacket& event);

  // Posts the event onto |outbound_events_| after |delay| ms. A call to
  // |PostEventResponse| with |delay| 0 is equivalent to a call to |PostEvent|.
  void PostDelayedEventResponse(const EventPacket& event,
                                std::chrono::milliseconds delay);

  void OnFileCanReadWithoutBlocking(int fd);
  // Called when there is a command to read on |fd|.
  void OnCommandReady(int fd);

 private:
  // Reads in a command packet and calls the command ready callback,
  // |command_handler_|, passing ownership of the command packet to the handler.
  void ReceiveReadyCommand() const;

  // Callback executed in ReceiveReadyCommand() to pass the incoming command
  // over to the handler for further processing.
  std::function<void(std::unique_ptr<CommandPacket>)> command_handler_;

  // Callbacks to schedule events.
  std::function<void(std::chrono::milliseconds, const TaskCallback&)>
      schedule_event_;
  std::function<void(std::chrono::milliseconds,
                     std::chrono::milliseconds,
                     const TaskCallback&)>
      schedule_periodic_event_;

  // For performing packet-based IO.
  PacketStream packet_stream_;

+69 −17
Original line number Diff line number Diff line
@@ -73,6 +73,11 @@ bool ParseUint16t(const base::StringPiece& value, uint16_t* field) {

namespace test_vendor_lib {

void DualModeController::AddControllerEvent(std::chrono::milliseconds delay,
                                            const TaskCallback& task) {
  controller_events_.push_back(schedule_task_(delay, task));
}

void DualModeController::SendCommandCompleteSuccess(
    const uint16_t command_opcode) const {
  send_event_(EventPacket::CreateCommandCompleteOnlyStatusEvent(
@@ -179,6 +184,24 @@ void DualModeController::RegisterHandlersWithTestChannelTransport(
      });
}

void DualModeController::RegisterTaskScheduler(
    std::function<AsyncTaskId(std::chrono::milliseconds, const TaskCallback&)>
        oneshotScheduler) {
  schedule_task_ = oneshotScheduler;
}

void DualModeController::RegisterPeriodicTaskScheduler(
    std::function<AsyncTaskId(std::chrono::milliseconds,
                              std::chrono::milliseconds,
                              const TaskCallback&)> periodicScheduler) {
  schedule_periodic_task_ = periodicScheduler;
}

void DualModeController::RegisterTaskCancel(
    std::function<void(AsyncTaskId)> task_cancel) {
  cancel_task_ = task_cancel;
}

void DualModeController::HandleTestChannelCommand(
    const std::string& name, const vector<std::string>& args) {
  if (active_test_channel_commands_.count(name) == 0)
@@ -209,26 +232,42 @@ void DualModeController::RegisterEventChannel(
  send_event_ = callback;
}

void DualModeController::RegisterDelayedEventChannel(
    const std::function<void(std::unique_ptr<EventPacket>,
                             std::chrono::milliseconds)>& callback) {
  send_delayed_event_ = callback;
  SetEventDelay(0);
void DualModeController::HandleTimerTick() {
  // PageScan();
  if (le_scan_enable_)
    LOG_ERROR(LOG_TAG, "LE scan");
  // LeScan();
}

void DualModeController::SetEventDelay(int64_t delay) {
  if (delay < 0)
    delay = 0;
  send_event_ = [this, delay](std::unique_ptr<EventPacket> arg) {
    send_delayed_event_(std::move(arg), std::chrono::milliseconds(delay));
  };
void DualModeController::SetTimerPeriod(std::chrono::milliseconds new_period) {
  timer_period_ = new_period;

  if (timer_tick_task_ == 0)
    return;

  // Restart the timer with the new period
  StopTimer();
  StartTimer();
}

void DualModeController::StartTimer() {
  LOG_ERROR(LOG_TAG, "StartTimer");
  timer_tick_task_ = schedule_periodic_task_(
      std::chrono::milliseconds(0), timer_period_, [this]() {
        DualModeController::HandleTimerTick();
      });
}

void DualModeController::StopTimer() {
  LOG_ERROR(LOG_TAG, "StopTimer");
  cancel_task_(timer_tick_task_);
  timer_tick_task_ = 0;
}

void DualModeController::TestChannelClear(
    UNUSED_ATTR const vector<std::string>& args) {
  LogCommand("TestChannel Clear");
  test_channel_state_ = kNone;
  SetEventDelay(0);
}

void DualModeController::TestChannelDiscover(
@@ -249,22 +288,27 @@ void DualModeController::TestChannelTimeoutAll(
}

void DualModeController::TestChannelSetEventDelay(
    const vector<std::string>& args) {
    const vector<std::string>& args UNUSED_ATTR) {
  LogCommand("TestChannel Set Event Delay");
  test_channel_state_ = kDelayedResponse;
  SetEventDelay(std::stoi(args[1]));
}

void DualModeController::TestChannelClearEventDelay(
    UNUSED_ATTR const vector<std::string>& args) {
  LogCommand("TestChannel Clear Event Delay");
  test_channel_state_ = kNone;
  SetEventDelay(0);
}

void DualModeController::HciReset(UNUSED_ATTR const vector<uint8_t>& args) {
  LogCommand("Reset");
  state_ = kStandby;
  if (timer_tick_task_ != 0) {
    LOG_INFO(LOG_TAG, "The timer was already running!");
    StopTimer();
  }
  LOG_INFO(LOG_TAG, "Starting timer.");
  StartTimer();

  SendCommandCompleteSuccess(HCI_RESET);
}

@@ -494,8 +538,8 @@ void DualModeController::HciInquiry(const vector<uint8_t>& args) {
      /* TODO: Return responses from modeled devices */
    } break;
  }
  send_delayed_event_(EventPacket::CreateInquiryCompleteEvent(kSuccessStatus),
                      std::chrono::milliseconds(args[4] * 1280));
  AddControllerEvent(std::chrono::milliseconds(args[4] * 1280),
                     [this]() { DualModeController::InquiryTimeout(); });
}

void DualModeController::HciInquiryCancel(
@@ -506,6 +550,14 @@ void DualModeController::HciInquiryCancel(
  SendCommandCompleteSuccess(HCI_INQUIRY_CANCEL);
}

void DualModeController::InquiryTimeout() {
  LOG_INFO(LOG_TAG, "InquiryTimer fired");
  if (state_ == kInquiry) {
    state_ = kStandby;
    send_event_(EventPacket::CreateInquiryCompleteEvent(kSuccessStatus));
  }
}

void DualModeController::HciDeleteStoredLinkKey(
    UNUSED_ATTR const vector<uint8_t>& args) {
  LogCommand("Delete Stored Link Key");
+11 −64
Original line number Diff line number Diff line
@@ -16,16 +16,13 @@

#define LOG_TAG "hci_transport"

#include <cinttypes>

#include "hci_transport.h"

extern "C" {
#include <assert.h>
#include <sys/socket.h>

#include "osi/include/log.h"
#include "stack/include/hcidefs.h"
}  // extern "C"

namespace test_vendor_lib {

@@ -49,7 +46,7 @@ int HciTransport::GetVendorFd() const {

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;
@@ -58,92 +55,42 @@ bool HciTransport::SetUp() {
  return true;
}

void HciTransport::OnFileCanReadWithoutBlocking(int fd) {
void HciTransport::OnCommandReady(int fd) {
  CHECK(fd == GetVendorFd());
  LOG_INFO(LOG_TAG, "Event ready in HciTransport on fd: %d.", fd);
  LOG_VERBOSE(LOG_TAG, "Command 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();
      command_handler_(packet_stream_.ReceiveCommand(fd));
      break;
    }

    case (DATA_TYPE_ACL): {
      LOG_INFO(LOG_TAG, "ACL data packets not currently supported.");
      LOG_ERROR(LOG_TAG, "ACL data packets not currently supported.");
      break;
    }

    case (DATA_TYPE_SCO): {
      LOG_INFO(LOG_TAG, "SCO data packets not currently supported.");
      LOG_ERROR(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.");
      LOG_ERROR(LOG_TAG, "Error received an invalid packet type from the HCI.");
      assert(packet_type == DATA_TYPE_COMMAND);
      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(
    const std::function<void(std::unique_ptr<CommandPacket>)>& callback) {
  command_handler_ = callback;
}

void HciTransport::RegisterEventScheduler(
    const std::function<void(std::chrono::milliseconds, const TaskCallback&)>&
        evtScheduler) {
  schedule_event_ = evtScheduler;
}

void HciTransport::RegisterPeriodicEventScheduler(
    const std::function<void(std::chrono::milliseconds,
                             std::chrono::milliseconds,
                             const TaskCallback&)>& periodicEvtScheduler) {
  schedule_periodic_event_ = periodicEvtScheduler;
}

void HciTransport::PostEventResponse(const EventPacket& event) {
  schedule_event_(std::chrono::milliseconds(0), [this, event]() {
    packet_stream_.SendEvent(event, GetVendorFd());
  });
}

void HciTransport::PostDelayedEventResponse(const EventPacket& event,
                                            std::chrono::milliseconds 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.");
    schedule_event_(std::chrono::milliseconds(0), [this, event]() {
      packet_stream_.SendEvent(event, GetVendorFd());
    });
  }

  LOG_INFO(LOG_TAG,
           "Posting event response with delay of %" PRId64 " ms.",
           static_cast<int64_t>(std::chrono::milliseconds(delay).count()));

  schedule_event_(delay, [this, event]() {
void HciTransport::PostEvent(const EventPacket& event) {
  packet_stream_.SendEvent(event, GetVendorFd());
  });
}

}  // namespace test_vendor_lib
+13 −8
Original line number Diff line number Diff line
@@ -41,25 +41,30 @@ bool VendorManager::Initialize() {
  controller_.RegisterHandlersWithTestChannelTransport(test_channel_transport_);

  controller_.RegisterEventChannel([this](std::unique_ptr<EventPacket> event) {
    transport_.PostEventResponse(*event);
    EventPacket evt = *event;
    async_manager_.ExecAsync(std::chrono::milliseconds(0), [evt,this]() {
      transport_.PostEvent(evt);
    });
  });

  transport_.RegisterEventScheduler(
  controller_.RegisterTaskScheduler(
      [this](std::chrono::milliseconds delay, const TaskCallback& task) {
        async_manager_.ExecAsync(delay, task);
        return async_manager_.ExecAsync(delay, task);
      });

  transport_.RegisterPeriodicEventScheduler(
  controller_.RegisterPeriodicTaskScheduler(
      [this](std::chrono::milliseconds delay,
             std::chrono::milliseconds period,
             const TaskCallback& task) {
        async_manager_.ExecAsyncPeriodically(delay, period, task);
        return async_manager_.ExecAsyncPeriodically(delay, period, task);
      });

  controller_.RegisterTaskCancel(
      [this](AsyncTaskId task) { async_manager_.CancelAsyncTask(task); });

  if (async_manager_.WatchFdForNonBlockingReads(
          transport_.GetVendorFd(), [this](int fd) {
            transport_.OnFileCanReadWithoutBlocking(fd);
          }) != 0) {
          transport_.GetVendorFd(),
          [this](int fd) { transport_.OnCommandReady(fd); }) != 0) {
    LOG_ERROR(LOG_TAG, "Error watching vendor fd.");
    return true;
  }
Loading