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

Commit e9225a86 authored by Hansong Zhang's avatar Hansong Zhang
Browse files

L2cap ERTM: Enter local busy on EnqueueBuffer full

* Allow EnqueueBuffer user to check current buffer size and register for
a callback when buffer is empty.
* In ERTM DataController, enter/clear local busy condition when it
detects EnqueueBuffer full (contains 2 items so far). Note that there is
a buffer in Queue itself.
* Add corresponding PTS test case.

Test: cert/run --host
Change-Id: Id2f7e745f010cbebfdc2ed61920ad3baec7968f3
parent e8b60c22
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -49,6 +49,11 @@ class PyL2capChannel(IEventStream):
        self._device.l2cap.CloseChannel(
            l2cap_facade_pb2.CloseChannelRequest(psm=self._psm))

    def set_traffic_paused(self, paused):
        self._device.l2cap.SetTrafficPaused(
            l2cap_facade_pb2.SetTrafficPausedRequest(
                psm=self._psm, paused=paused))


class _ClassicConnectionResponseFutureWrapper(object):
    """
@@ -102,6 +107,9 @@ class PyL2cap(Closable):
        return _ClassicConnectionResponseFutureWrapper(
            response_future, self._device, psm, self._l2cap_stream)

    def get_channel_queue_buffer_size(self):
        return self._device.l2cap.GetChannelQueueDepth(empty_proto.Empty()).size


class PyLeL2capFixedChannel(IEventStream):

+90 −0
Original line number Diff line number Diff line
@@ -883,6 +883,47 @@ class L2capTest(GdBaseTestClass):

        cert_channel.send_i_frame(tx_seq=1, req_seq=2, payload=SAMPLE_PACKET)

    @metadata(
        pts_test_id="L2CAP/ERM/BV-07-C", pts_test_name="Send S-Frame [RNR]")
    def test_send_s_frame_rnr(self):
        """
        Verify the IUT sends an S-frame [RNR] when it detects local busy condition
        NOTE: In GD stack, we enter local busy condition if client doesn't dequeue
        and packets are accumulating in buffer
        """
        self._setup_link_from_cert()

        config = CertL2cap.config_option_ertm(
            fcs=FcsType.NO_FCS, tx_window_size=10)

        (dut_channel, cert_channel) = self._open_channel_from_cert(
            mode=RetransmissionFlowControlMode.ERTM,
            fcs=FcsType.NO_FCS,
            req_config_options=config,
            rsp_config_options=config)

        dut_channel.send(b'abc')
        assertThat(cert_channel).emits(
            L2capMatchers.IFrame(tx_seq=0, payload=b'abc'))
        cert_channel.send_i_frame(tx_seq=0, req_seq=1, payload=SAMPLE_PACKET)

        dut_channel.set_traffic_paused(True)

        buffer_size = self.dut_l2cap.get_channel_queue_buffer_size()

        for i in range(buffer_size):
            cert_channel.send_i_frame(
                tx_seq=i + 1, req_seq=1, payload=SAMPLE_PACKET)
            assertThat(cert_channel).emits(
                L2capMatchers.SFrame(
                    s=l2cap_packets.SupervisoryFunction.RECEIVER_READY))

        cert_channel.send_i_frame(
            tx_seq=buffer_size + 1, req_seq=1, payload=SAMPLE_PACKET)
        assertThat(cert_channel).emits(
            L2capMatchers.SFrame(
                s=l2cap_packets.SupervisoryFunction.RECEIVER_NOT_READY))

    @metadata(
        pts_test_id="L2CAP/ERM/BV-08-C",
        pts_test_name="Send S-Frame [RR] with Poll Bit Set")
@@ -1205,6 +1246,55 @@ class L2capTest(GdBaseTestClass):
            f=Final.POLL_RESPONSE)
        assertThat(cert_channel).emitsNone(L2capMatchers.IFrame(tx_seq=0))

    @metadata(
        pts_test_id="L2CAP/ERM/BV-22-C",
        pts_test_name="Exit Local Busy Condition")
    def test_exit_local_busy_condition(self):
        """
        Verify the IUT sends an S-frame [RR] Poll = 1 when the local busy condition is cleared
        NOTE: In GD stack, we enter local busy condition if client doesn't dequeue
        and packets are accumulating in buffer
        """
        self._setup_link_from_cert()

        config = CertL2cap.config_option_ertm(
            fcs=FcsType.NO_FCS, tx_window_size=10)

        (dut_channel, cert_channel) = self._open_channel_from_cert(
            mode=RetransmissionFlowControlMode.ERTM,
            fcs=FcsType.NO_FCS,
            req_config_options=config,
            rsp_config_options=config)

        dut_channel.send(b'abc')
        assertThat(cert_channel).emits(
            L2capMatchers.IFrame(tx_seq=0, payload=b'abc'))
        cert_channel.send_i_frame(tx_seq=0, req_seq=1, payload=SAMPLE_PACKET)

        dut_channel.set_traffic_paused(True)

        buffer_size = self.dut_l2cap.get_channel_queue_buffer_size()

        for i in range(buffer_size):
            cert_channel.send_i_frame(
                tx_seq=i + 1, req_seq=1, payload=SAMPLE_PACKET)
            assertThat(cert_channel).emits(
                L2capMatchers.SFrame(
                    s=l2cap_packets.SupervisoryFunction.RECEIVER_READY))

        cert_channel.send_i_frame(
            tx_seq=buffer_size + 1, req_seq=1, payload=SAMPLE_PACKET)
        assertThat(cert_channel).emits(
            L2capMatchers.SFrame(
                s=l2cap_packets.SupervisoryFunction.RECEIVER_NOT_READY))

        dut_channel.set_traffic_paused(False)
        assertThat(cert_channel).emits(
            L2capMatchers.SFrame(
                s=l2cap_packets.SupervisoryFunction.RECEIVER_READY,
                p=l2cap_packets.Poll.POLL))
        cert_channel.send_s_frame(1, f=l2cap_packets.Final.POLL_RESPONSE)

    @metadata(
        pts_test_id="L2CAP/ERM/BV-23-C",
        pts_test_name="Transmit I-Frames using SAR")
+41 −2
Original line number Diff line number Diff line
@@ -108,6 +108,27 @@ class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service
    return ::grpc::Status::OK;
  }

  ::grpc::Status SetTrafficPaused(::grpc::ServerContext* context, const SetTrafficPausedRequest* request,
                                  ::google::protobuf::Empty* response) override {
    auto psm = request->psm();
    if (dynamic_channel_helper_map_.find(request->psm()) == dynamic_channel_helper_map_.end()) {
      return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered");
    }
    if (request->paused()) {
      dynamic_channel_helper_map_[psm]->SuspendDequeue();
    } else {
      dynamic_channel_helper_map_[psm]->ResumeDequeue();
    }
    return ::grpc::Status::OK;
  }

  ::grpc::Status GetChannelQueueDepth(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
                                      GetChannelQueueDepthResponse* response) override {
    // Use the value kChannelQueueSize (5) in internal/dynamic_channel_impl.h
    response->set_size(5);
    return ::grpc::Status::OK;
  }

  class L2capDynamicChannelHelper {
   public:
    L2capDynamicChannelHelper(L2capClassicModuleFacadeService* service, L2capClassicModule* l2cap_layer,
@@ -124,7 +145,7 @@ class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service
    }

    ~L2capDynamicChannelHelper() {
      if (channel_ != nullptr) {
      if (dequeue_registered_) {
        channel_->GetQueueUpEnd()->UnregisterDequeue();
        channel_ = nullptr;
      }
@@ -171,6 +192,7 @@ class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service
      channel_->RegisterOnCloseCallback(
          facade_service_->facade_handler_,
          common::BindOnce(&L2capDynamicChannelHelper::on_close_callback, common::Unretained(this)));
      dequeue_registered_ = true;
      channel_->GetQueueUpEnd()->RegisterDequeue(
          facade_service_->facade_handler_,
          common::Bind(&L2capDynamicChannelHelper::on_incoming_packet, common::Unretained(this)));
@@ -179,8 +201,10 @@ class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service
    void on_close_callback(hci::ErrorCode error_code) {
      {
        std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
        if (dequeue_registered_.exchange(false)) {
          channel_->GetQueueUpEnd()->UnregisterDequeue();
        }
      }
      classic::ConnectionCloseEvent event;
      event.mutable_remote()->set_address(channel_->GetDevice().GetAddress().ToString());
      event.set_reason(static_cast<uint32_t>(error_code));
@@ -188,6 +212,20 @@ class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service
      channel_ = nullptr;
    }

    void SuspendDequeue() {
      if (dequeue_registered_.exchange(false)) {
        channel_->GetQueueUpEnd()->UnregisterDequeue();
      }
    }

    void ResumeDequeue() {
      if (!dequeue_registered_.exchange(true)) {
        channel_->GetQueueUpEnd()->RegisterDequeue(
            facade_service_->facade_handler_,
            common::Bind(&L2capDynamicChannelHelper::on_incoming_packet, common::Unretained(this)));
      }
    }

    void on_connect_fail(DynamicChannelManager::ConnectionResult result) {}

    void on_incoming_packet() {
@@ -237,6 +275,7 @@ class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service
    std::unique_ptr<DynamicChannel> channel_ = nullptr;
    Psm psm_;
    RetransmissionFlowControlMode mode_ = RetransmissionFlowControlMode::BASIC;
    std::atomic_bool dequeue_registered_ = false;
    std::condition_variable channel_open_cv_;
    std::mutex channel_open_cv_mutex_;
  };
+14 −0
Original line number Diff line number Diff line
@@ -17,6 +17,11 @@ service L2capClassicModuleFacade {
  rpc FetchL2capData(google.protobuf.Empty) returns (stream L2capPacket) {}
  rpc SetDynamicChannel(SetEnableDynamicChannelRequest) returns (google.protobuf.Empty) {}
  rpc SendDynamicChannelPacket(DynamicChannelPacket) returns (google.protobuf.Empty) {}
  rpc SetTrafficPaused(SetTrafficPausedRequest) returns (google.protobuf.Empty) {}
  rpc GetChannelQueueDepth(google.protobuf.Empty) returns (GetChannelQueueDepthResponse) {
    // Get the buffer size of channel queue end for L2CAP user (how many packets we can buffer
    // before L2CAP user dequeues.
  }
}

message RegisterChannelRequest {
@@ -87,3 +92,12 @@ message DynamicChannelPacket {
  uint32 psm = 2;
  bytes payload = 3;
}

message SetTrafficPausedRequest {
  bool paused = 1;
  uint32 psm = 2;
}

message GetChannelQueueDepthResponse {
  uint32 size = 1;
}
+1 −1
Original line number Diff line number Diff line
@@ -84,7 +84,7 @@ class DynamicChannelImpl : public l2cap::internal::ChannelImpl {
  // Internal states
  bool closed_ = false;
  hci::ErrorCode close_reason_ = hci::ErrorCode::SUCCESS;
  static constexpr size_t kChannelQueueSize = 10;
  static constexpr size_t kChannelQueueSize = 5;
  common::BidiQueue<packet::PacketView<packet::kLittleEndian>, packet::BasePacketBuilder> channel_queue_{
      kChannelQueueSize};

Loading