Bluetooth: host: Document L2CAP channel send buffer sizes
Update the documentation for L2CAp Connection oriented channel send function to include better description of the L2CAP PDUs (Basic frames) and L2CAP SDUs (Credit-based frames) so that the application can better understand how to size the buffer pools and setting the RX mtu. Document stack behavior on RX path and how the application has to set up the channel in order to receive segmented packets. Document stack behavior on TX path for reserving either mandatory or optional header bytes. Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no>
This commit is contained in:
parent
6483e12a8a
commit
69613626ed
2 changed files with 74 additions and 18 deletions
|
@ -26,7 +26,7 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** L2CAP header size, used for buffer size calculations */
|
||||
/** L2CAP PDU header size, used for buffer size calculations */
|
||||
#define BT_L2CAP_HDR_SIZE 4
|
||||
|
||||
/** Maximum Transmission Unit (MTU) for an outgoing L2CAP PDU. */
|
||||
|
@ -49,14 +49,36 @@ extern "C" {
|
|||
/** L2CAP SDU header size, used for buffer size calculations */
|
||||
#define BT_L2CAP_SDU_HDR_SIZE 2
|
||||
|
||||
/** @brief Maximum Transmission Unit for an unsegmented outgoing L2CAP SDU.
|
||||
*
|
||||
* The Maximum Transmission Unit for an outgoing L2CAP SDU when sent without
|
||||
* segmentation, i.e a single L2CAP SDU will fit inside a single L2CAP PDU.
|
||||
*
|
||||
* The MTU for outgoing L2CAP SDUs with segmentation is defined by the
|
||||
* size of the application buffer pool.
|
||||
*/
|
||||
#define BT_L2CAP_SDU_TX_MTU (BT_L2CAP_TX_MTU - BT_L2CAP_SDU_HDR_SIZE)
|
||||
|
||||
/** @brief Maximum Transmission Unit for an unsegmented incoming L2CAP SDU.
|
||||
*
|
||||
* The Maximum Transmission Unit for an incoming L2CAP SDU when sent without
|
||||
* segmentation, i.e a single L2CAP SDU will fit inside a single L2CAP PDU.
|
||||
*
|
||||
* The MTU for incoming L2CAP SDUs with segmentation is defined by the
|
||||
* size of the application buffer pool. The application will have to define
|
||||
* an alloc_buf callback for the channel in order to support receiving
|
||||
* segmented L2CAP SDUs.
|
||||
*/
|
||||
#define BT_L2CAP_SDU_RX_MTU (BT_L2CAP_RX_MTU - BT_L2CAP_SDU_HDR_SIZE)
|
||||
|
||||
/** @def BT_L2CAP_SDU_BUF_SIZE
|
||||
*
|
||||
* @brief Helper to calculate needed buffer size for L2CAP SDUs.
|
||||
* Useful for creating buffer pools.
|
||||
*
|
||||
* @param mtu Needed L2CAP SDU MTU.
|
||||
* @param mtu Required BT_L2CAP_*_SDU.
|
||||
*
|
||||
* @return Needed buffer size to match the requested L2CAP MTU.
|
||||
* @return Needed buffer size to match the requested L2CAP SDU MTU.
|
||||
*/
|
||||
#define BT_L2CAP_SDU_BUF_SIZE(mtu) BT_L2CAP_BUF_SIZE(BT_L2CAP_SDU_HDR_SIZE + (mtu))
|
||||
|
||||
|
@ -148,7 +170,14 @@ struct bt_l2cap_le_endpoint {
|
|||
struct bt_l2cap_le_chan {
|
||||
/** Common L2CAP channel reference object */
|
||||
struct bt_l2cap_chan chan;
|
||||
/** Channel Receiving Endpoint */
|
||||
/** @brief Channel Receiving Endpoint.
|
||||
*
|
||||
* If the application has set an alloc_buf channel callback for the
|
||||
* channel to support receiving segmented L2CAP SDUs the application
|
||||
* should inititalize the MTU of the Receiving Endpoint. Otherwise the
|
||||
* MTU of the receiving endpoint will be initialized to
|
||||
* @ref BT_L2CAP_SDU_RX_MTU by the stack.
|
||||
*/
|
||||
struct bt_l2cap_le_endpoint rx;
|
||||
/** Channel Transmission Endpoint */
|
||||
struct bt_l2cap_le_endpoint tx;
|
||||
|
@ -240,6 +269,8 @@ struct bt_l2cap_chan_ops {
|
|||
* If this callback is provided the channel will use it to allocate
|
||||
* buffers to store incoming data. Channels that requires segmentation
|
||||
* must set this callback.
|
||||
* If the application has not set a callback the L2CAP SDU MTU will be
|
||||
* truncated to @ref BT_L2CAP_SDU_RX_MTU.
|
||||
*
|
||||
* @param chan The channel requesting a buffer.
|
||||
*
|
||||
|
@ -290,10 +321,15 @@ struct bt_l2cap_chan_ops {
|
|||
};
|
||||
|
||||
/** @def BT_L2CAP_CHAN_SEND_RESERVE
|
||||
* @brief Headroom needed for outgoing buffers
|
||||
* @brief Headroom needed for outgoing L2CAP PDUs.
|
||||
*/
|
||||
#define BT_L2CAP_CHAN_SEND_RESERVE (BT_L2CAP_BUF_SIZE(0))
|
||||
|
||||
/** @def BT_L2CAP_SDU_CHAN_SEND_RESERVE
|
||||
* @brief Headroom needed for outgoing L2CAP SDUs.
|
||||
*/
|
||||
#define BT_L2CAP_SDU_CHAN_SEND_RESERVE (BT_L2CAP_SDU_BUF_SIZE(0))
|
||||
|
||||
/** @brief L2CAP Server structure. */
|
||||
struct bt_l2cap_server {
|
||||
/** @brief Server PSM.
|
||||
|
@ -421,6 +457,25 @@ int bt_l2cap_chan_disconnect(struct bt_l2cap_chan *chan);
|
|||
* Regarding to first input parameter, to get details see reference description
|
||||
* to bt_l2cap_chan_connect() API above.
|
||||
*
|
||||
* When sending L2CAP data over an BR/EDR connection the application is sending
|
||||
* L2CAP PDUs. The application is required to have reserved
|
||||
* @ref BT_L2CAP_CHAN_SEND_RESERVE bytes in the buffer before sending.
|
||||
* The application should use the @def BT_L2CAP_BUF_SIZE helper to correctly
|
||||
* size the buffers for the for the outgoing buffer pool.
|
||||
*
|
||||
* When sending L2CAP data over an LE connection the applicatios is sending
|
||||
* L2CAP SDUs. The application can optionally reserve
|
||||
* @ref BT_L2CAP_SDU_CHAN_SEND_RESERVE bytes in the buffer before sending.
|
||||
* By reserving bytes in the buffer the stack can use this buffer as a segment
|
||||
* directly, otherwise it will have to allocate a new segment for the first
|
||||
* segment.
|
||||
* If the application is reserving the bytes it should use the
|
||||
* @def BT_L2CAP_BUF_SIZE helper to correctly size the buffers for the for the
|
||||
* outgoing buffer pool.
|
||||
* When segmenting an L2CAP SDU into L2CAP PDUs the stack will first attempt
|
||||
* to allocate buffers from the original buffer pool of the L2CAP SDU before
|
||||
* using the stacks own buffer pool.
|
||||
*
|
||||
* @note Buffer ownership is transferred to the stack in case of success, in
|
||||
* case of an error the caller retains the ownership of the buffer.
|
||||
*
|
||||
|
|
|
@ -60,11 +60,6 @@ NET_BUF_POOL_FIXED_DEFINE(disc_pool, 1,
|
|||
sizeof(struct bt_l2cap_disconn_req)),
|
||||
NULL);
|
||||
|
||||
/* Maximum Packet Size (MPS) is defined as the MTU of an L2CAP Basic Frame. */
|
||||
#define L2CAP_MAX_LE_MPS BT_L2CAP_RX_MTU
|
||||
/* For now use MPS - SDU length to disable segmentation */
|
||||
#define L2CAP_MAX_LE_MTU (L2CAP_MAX_LE_MPS - BT_L2CAP_SDU_HDR_SIZE)
|
||||
|
||||
#define L2CAP_ECRED_CHAN_MAX 5
|
||||
|
||||
#define l2cap_lookup_ident(conn, ident) __l2cap_lookup_ident(conn, ident, false)
|
||||
|
@ -801,20 +796,25 @@ static void l2cap_chan_rx_init(struct bt_l2cap_le_chan *chan)
|
|||
|
||||
/* Use existing MTU if defined */
|
||||
if (!chan->rx.mtu) {
|
||||
chan->rx.mtu = L2CAP_MAX_LE_MTU;
|
||||
/* If application has not provide the incoming L2CAP SDU MTU use
|
||||
* an MTU that does not require segmentation.
|
||||
*/
|
||||
chan->rx.mtu = BT_L2CAP_SDU_RX_MTU;
|
||||
}
|
||||
|
||||
/* MPS shall not be bigger than MTU + 2 as the remaining bytes cannot
|
||||
* be used.
|
||||
/* MPS shall not be bigger than MTU + BT_L2CAP_SDU_HDR_SIZE as the
|
||||
* remaining bytes cannot be used.
|
||||
*/
|
||||
chan->rx.mps = MIN(chan->rx.mtu + 2, L2CAP_MAX_LE_MPS);
|
||||
chan->rx.mps = MIN(chan->rx.mtu + BT_L2CAP_SDU_HDR_SIZE,
|
||||
BT_L2CAP_RX_MTU);
|
||||
|
||||
/* Truncate MTU if channel have disabled segmentation but still have
|
||||
* set an MTU which requires it.
|
||||
*/
|
||||
if (!chan->chan.ops->alloc_buf && (chan->rx.mps < chan->rx.mtu + 2)) {
|
||||
if (!chan->chan.ops->alloc_buf &&
|
||||
(chan->rx.mps < chan->rx.mtu + BT_L2CAP_SDU_HDR_SIZE)) {
|
||||
BT_WARN("Segmentation disabled but MTU > MPS, truncating MTU");
|
||||
chan->rx.mtu = chan->rx.mps - 2;
|
||||
chan->rx.mtu = chan->rx.mps - BT_L2CAP_SDU_HDR_SIZE;
|
||||
}
|
||||
|
||||
/* Use existing credits if defined */
|
||||
|
@ -823,7 +823,7 @@ static void l2cap_chan_rx_init(struct bt_l2cap_le_chan *chan)
|
|||
/* Auto tune credits to receive a full packet */
|
||||
chan->rx.init_credits =
|
||||
ceiling_fraction(chan->rx.mtu,
|
||||
L2CAP_MAX_LE_MPS);
|
||||
BT_L2CAP_RX_MTU);
|
||||
} else {
|
||||
chan->rx.init_credits = L2CAP_LE_MAX_CREDITS;
|
||||
}
|
||||
|
@ -832,7 +832,8 @@ static void l2cap_chan_rx_init(struct bt_l2cap_le_chan *chan)
|
|||
atomic_set(&chan->rx.credits, 0);
|
||||
|
||||
if (BT_DBG_ENABLED &&
|
||||
chan->rx.init_credits * chan->rx.mps < chan->rx.mtu + 2) {
|
||||
chan->rx.init_credits * chan->rx.mps <
|
||||
chan->rx.mtu + BT_L2CAP_SDU_HDR_SIZE) {
|
||||
BT_WARN("Not enough credits for a full packet");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue