blob: 9851896b8c8d20380f0d4b7282139759eaa26cb7 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef GARNET_DRIVERS_BLUETOOTH_LIB_L2CAP_PDU_H_
#define GARNET_DRIVERS_BLUETOOTH_LIB_L2CAP_PDU_H_
#include <endian.h>
#include <fbl/intrusive_double_list.h>
#include <zircon/assert.h>
#include "garnet/drivers/bluetooth/lib/hci/acl_data_packet.h"
#include "garnet/drivers/bluetooth/lib/l2cap/l2cap.h"
#include "lib/fxl/macros.h"
namespace btlib {
namespace l2cap {
// Represents a L2CAP PDU. Each PDU contains a complete L2CAP frame that can be
// transmitted over a L2CAP channel. PDUs are the building blocks for SDUs
// (higher-layer service data units).
//
// A PDU is composed of one or more fragments, each contained in a HCI ACL data
// packet. A PDU cannot be populated directly and must be obtained from a
// Recombiner or Fragmenter.
//
// A PDU instance is light-weight (it consists of a single unique_ptr via
// common::LinkedList and a size_t field) and can be passed around by value.
// As the PDU uniquely owns its chain of fragments, a PDU is move-only.
//
// THREAD-SAFETY:
//
// This class is not thread-safe. External locking should be provided if an
// instance will be accessed on multiple threads.
class PDU final {
public:
using FragmentList = common::LinkedList<hci::ACLDataPacket>;
// Reader allows sequential access to the payload of a (B-frame) PDU using no
// dynamic allocations and as little copying as possible.
//
// A Reader is valid as long as the underlying PDU is valid. Deleting or
// invalidating a PDU (e.g. via ReleaseFragments()) while using a Reader will
// lead to undefined behavior.
//
// As a special-case, however, it is permitted to invalidate the PDU in the
// ReadFunc, so long as no further reference is made to the PDU, the
// ByteBuffer, or the Reader. (Said differently: the ReadFunc may assume that
// it is invoked as a tail-call.)
class Reader final {
public:
explicit Reader(const PDU* pdu);
// Calls |func| with the next segment of data with the given |size|. Returns
// false if less than |size| bytes remain in the PDU or if |size| is 0.
//
// TODO(armansito): Allow jumping to an offset. With that we can remove
// PDU::Copy() and PDU::ViewFirstFragment().
using ReadFunc = fit::function<void(const common::ByteBuffer& data)>;
bool ReadNext(size_t size, const ReadFunc& func);
private:
size_t offset_;
size_t frag_offset_;
const PDU* pdu_;
FragmentList::const_iterator cur_fragment_;
};
PDU();
~PDU() = default;
// Allow move operations.
PDU(PDU&& other);
PDU& operator=(PDU&& other);
// An unpopulated PDU is considered invalid, which is the default-constructed
// state.
bool is_valid() const {
ZX_DEBUG_ASSERT(fragments_.is_empty() && !fragment_count_ ||
!fragments_.is_empty() && fragment_count_);
return !fragments_.is_empty();
}
// The number of ACL data fragments that are currently a part of this PDU.
size_t fragment_count() const { return fragment_count_; }
// Returns the number of bytes that are currently contained in this PDU,
// excluding the Basic L2CAP header.
uint16_t length() const { return le16toh(basic_header().length); }
// The L2CAP channel that this packet belongs to.
ChannelId channel_id() const { return le16toh(basic_header().channel_id); }
// The connection handle that identifies the logical link this PDU is intended
// for.
hci::ConnectionHandle connection_handle() const {
ZX_DEBUG_ASSERT(is_valid());
return fragments_.begin()->connection_handle();
}
// This method will attempt to read |size| bytes of the basic-frame
// information payload (i.e. contents of this PDU excludng the basic L2CAP
// header) starting at offset |pos| and copy the contents into |out_buffer|.
//
// The amount read may be smaller then the amount requested if the PDU does
// not have enough data. |out_buffer| should be sufficiently large.
//
// Returns the number of bytes copied into |out_buffer|.
//
// NOTE: Use this method wisely as it can be costly. In particular, large
// values of |pos| will incur a cost (O(pos)) as the underlying fragments need
// to be traversed to find the initial fragment.
size_t Copy(common::MutableByteBuffer* out_buffer, size_t pos = 0,
size_t size = std::numeric_limits<std::size_t>::max()) const;
// Helper for directly reading the contents of the first ACL data fragment of
// this PDU without copying. If the PDU contains multiple ACL data fragments
// and |size| is larger than the first fragment, the returned view will only
// contain the first fragment.
const common::BufferView ViewFirstFragment(size_t size) const;
// Release ownership of the current fragments, moving them to the caller. Once
// this is called, the PDU will become invalid.
FragmentList ReleaseFragments();
private:
friend class Reader;
friend class Fragmenter;
friend class Recombiner;
// Methods accessed by friends.
const BasicHeader& basic_header() const;
// Takes ownership of |fragment| and adds it to |fragments_|. This method
// assumes that validity checks on |fragment| have already been performed.
void AppendFragment(hci::ACLDataPacketPtr fragment);
// The number of fragments currently stored in this PDU.
size_t fragment_count_;
// ACL data fragments that currently form this PDU. In a complete PDU, it is
// expected that the sum of the payload sizes of all elements in |fragments_|
// is equal to the length of the frame (i.e. length() + sizeof(BasicHeader)).
FragmentList fragments_;
FXL_DISALLOW_COPY_AND_ASSIGN(PDU);
};
} // namespace l2cap
} // namespace btlib
#endif // GARNET_DRIVERS_BLUETOOTH_LIB_L2CAP_PDU_H_