This patch adds API for LE Connection Oriented Channels. Note that implementation is hided behind BLE_L2CAP_COC_MAX_NUM flag which defines maximum number of supported dynamic channels
Overview: Idea is that credits are hidden from the user and controlled by the stack. User creates its own memory pool for SDU taking into account SDU size and number of L2CAP LE COC channels he expect to use. User profiles SDU (os_mbuf) to the stack when creates or accepts connection. Flow overview. Outgoing connection: 1. *chan = ble_l2cap_connect(conn_handle, psm, mtu, ble_l2cap_chan_ops *ops, struct os_mbuf *sdu_rx); 2. ops->connected(*chan) callback is called when channel has been established. If connection has been rejected, ops->disconnected(*chan, coc_err) is called with coc_err which contains reason of reject 3. ops->recv(*chan, sdu_rx) callback is called on incoming data. Note, it is called when SDU is completed 3a. User needs to call ble_l2cap_recv_ready(*chan, sdu_rx) where sdu_rx is os_mbuf for next SDU. 4. To send data do remote device ble_l2cap_send(*chan, sdu_tx) shall be called 5. To drop channel ble_l2cap_disconnect(*chan) shall be called. Incoming connection: 1. ble_l2cap_create_server(psm, mtu, *ops, accept_cb) 2. accept_cb(conn_handle, peer_mtu, *chan) is called on create connection request if there is server for given PSM in the stack. 2a. User might want to check required security and MTU requirements before accepts connection. 2b. User needs to call ble_l2cap_recv_ready(*chan, sdu_rx) where sdu_rx is os_mbuf for next SDU. 2c. If accept_cb returns 0, connection is considered established. Open points: 1. As for now we assume that application is responsible for security on the link. We could consider to move this logic into l2cap but not sure if we want to do so -- Hi all, Waiting for comments on proposed API Best Regards Łukasz --- net/nimble/host/include/host/ble_l2cap.h | 33 ++++++++++++++++ net/nimble/host/src/ble_l2cap.c | 68 ++++++++++++++++++++++++++++++-- net/nimble/host/src/ble_l2cap_priv.h | 21 ++++++++++ net/nimble/host/syscfg.yml | 5 +++ 4 files changed, 124 insertions(+), 3 deletions(-) diff --git a/net/nimble/host/include/host/ble_l2cap.h b/net/nimble/host/include/host/ble_l2cap.h index 52849af..d63f5f2 100644 --- a/net/nimble/host/include/host/ble_l2cap.h +++ b/net/nimble/host/include/host/ble_l2cap.h @@ -56,6 +56,17 @@ struct ble_hs_conn; #define BLE_L2CAP_SIG_ERR_MTU_EXCEEDED 0x0001 #define BLE_L2CAP_SIG_ERR_INVALID_CID 0x0002 +#define BLE_L2CAP_COC_ERR_CONNECTION_SUCCESS 0x0000 +#define BLE_L2CAP_COC_ERR_UNKNOWN_LE_PSM 0x0002 +#define BLE_L2CAP_COC_ERR_NO_RESOURCES 0x0004 +#define BLE_L2CAP_COC_ERR_INSUFFICIENT_AUTHEN 0x0005 +#define BLE_L2CAP_COC_ERR_INSUFFICIENT_AUTHOR 0x0006 +#define BLE_L2CAP_COC_ERR_INSUFFICIENT_KEY_SZ 0x0007 +#define BLE_L2CAP_COC_ERR_INSUFFICIENT_ENC 0x0008 +#define BLE_L2CAP_COC_ERR_INVALID_SOURCE_CID 0x0009 +#define BLE_L2CAP_COC_ERR_SOURCE_CID_ALREADY_USED 0x000A +#define BLE_L2CAP_COC_ERR_UNACCEPTABLE_PARAMETERS 0x000B + typedef void ble_l2cap_sig_update_fn(uint16_t conn_handle, int status, void *arg); @@ -70,6 +81,28 @@ int ble_l2cap_sig_update(uint16_t conn_handle, struct ble_l2cap_sig_update_params *params, ble_l2cap_sig_update_fn *cb, void *cb_arg); +struct ble_l2cap_chan; + +struct ble_l2cap_chan_ops { + void (*connected)(struct ble_l2cap_chan *chan); + void (*disconnected)(struct ble_l2cap_chan *chan, uint16_t coc_err); + void (*recv)(struct ble_l2cap_chan *chan, struct os_mbuf *sdu_rx); +}; + +typedef int ble_l2cap_accept_fn(uint16_t conn_handle, uint16_t peer_mtu, + struct ble_l2cap_chan *chan); +int ble_l2cap_create_server(uint16_t psm, uint16_t mtu, + struct ble_l2cap_chan_ops *ops, + ble_l2cap_accept_fn *cb); + +struct ble_l2cap_chan *ble_l2cap_connect(uint16_t conn_handle, uint16_t psm, + uint16_t mtu, + struct ble_l2cap_chan_ops *ops, + struct os_mbuf *sdu_rx); +int ble_l2cap_disconnect(struct ble_l2cap_chan *chan); +int ble_l2cap_send(struct ble_l2cap_chan *chan, struct os_mbuf *sdu_tx); +void ble_l2cap_recv_ready(struct ble_l2cap_chan *chan, struct os_mbuf *sdu_rx); + #ifdef __cplusplus } #endif diff --git a/net/nimble/host/src/ble_l2cap.c b/net/nimble/host/src/ble_l2cap.c index 7f66b57..654573b 100644 --- a/net/nimble/host/src/ble_l2cap.c +++ b/net/nimble/host/src/ble_l2cap.c @@ -31,8 +31,9 @@ _Static_assert(sizeof (struct ble_l2cap_hdr) == BLE_L2CAP_HDR_SZ, struct os_mempool ble_l2cap_chan_pool; static os_membuf_t ble_l2cap_chan_mem[ - OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_L2CAP_MAX_CHANS), - sizeof (struct ble_l2cap_chan)) + OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_L2CAP_MAX_CHANS) + + MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM), + sizeof (struct ble_l2cap_chan)) ]; STATS_SECT_DECL(ble_l2cap_stats) ble_l2cap_stats; @@ -137,6 +138,65 @@ ble_l2cap_prepend_hdr(struct os_mbuf *om, uint16_t cid, uint16_t len) return om; } +int +ble_l2cap_create_server(uint16_t psm, uint16_t mtu, + struct ble_l2cap_chan_ops *ops, + ble_l2cap_accept_fn *cb) +{ +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) == 0 + return BLE_HS_ENOTSUP; +#endif + + /*TODO: Create server object and put it on the queue */ + return BLE_HS_ENOTSUP; +} + +struct ble_l2cap_chan * +ble_l2cap_connect(uint16_t conn_handle, uint16_t psm, uint16_t mtu, + struct ble_l2cap_chan_ops *ops, struct os_mbuf *sdu_rx) +{ +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) == 0 + return NULL; +#endif + + /* + * TODO In here we are going to create l2cap channel and send + * BLE_L2CAP_SIG_OP_CREDIT_CONNECT_REQ + */ + return NULL; +} + +int ble_l2cap_disconnect(struct ble_l2cap_chan *chan) +{ +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) == 0 + return BLE_HS_ENOTSUP; +#endif + + /*TODO Implement */ + return BLE_HS_ENOTSUP; +} + +int +ble_l2cap_send(struct ble_l2cap_chan *chan, struct os_mbuf *sdu) +{ +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) == 0 + return BLE_HS_ENOTSUP; +#endif + + /*TODO Implement */ + return BLE_HS_ENOTSUP; +} + +void +ble_l2cap_recv_ready(struct ble_l2cap_chan *chan, struct os_mbuf *sdu_rx) +{ +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) == 0 + return; +#endif + + /*TODO In here we going to update sdu_rx buffer */ +} + static void ble_l2cap_forget_rx(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan) { @@ -337,7 +397,9 @@ ble_l2cap_init(void) { int rc; - rc = os_mempool_init(&ble_l2cap_chan_pool, MYNEWT_VAL(BLE_L2CAP_MAX_CHANS), + rc = os_mempool_init(&ble_l2cap_chan_pool, + MYNEWT_VAL(BLE_L2CAP_MAX_CHANS) + + MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM), sizeof (struct ble_l2cap_chan), ble_l2cap_chan_mem, "ble_l2cap_chan_pool"); if (rc != 0) { diff --git a/net/nimble/host/src/ble_l2cap_priv.h b/net/nimble/host/src/ble_l2cap_priv.h index 79d58a7..99ee9e9 100644 --- a/net/nimble/host/src/ble_l2cap_priv.h +++ b/net/nimble/host/src/ble_l2cap_priv.h @@ -63,6 +63,22 @@ typedef uint8_t ble_l2cap_chan_flags; typedef int ble_l2cap_rx_fn(uint16_t conn_handle, struct os_mbuf **rxom); +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) +struct ble_l2cap_coc_endpoint { + uint16_t cid; + uint16_t mtu; + uint16_t mps; + uint16_t credits; + struct os_mbuf *sdu; +}; + +struct ble_l2cap_coc_chan { + struct ble_l2cap_coc_endpoint rx; + struct ble_l2cap_coc_endpoint tx; +}; + +#endif + struct ble_l2cap_chan { SLIST_ENTRY(ble_l2cap_chan) blc_next; uint16_t blc_cid; @@ -75,6 +91,11 @@ struct ble_l2cap_chan { uint16_t blc_rx_len; /* Length of current reassembled rx packet. */ ble_l2cap_rx_fn *blc_rx_fn; + +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) + struct ble_l2cap_coc_chan blc_coc; + struct ble_l2cap_coc_chan_ops *blc_coc_ops; +#endif }; struct ble_l2cap_hdr { diff --git a/net/nimble/host/syscfg.yml b/net/nimble/host/syscfg.yml index a2504bf..62ca6f6 100644 --- a/net/nimble/host/syscfg.yml +++ b/net/nimble/host/syscfg.yml @@ -52,6 +52,11 @@ syscfg.defs: passes since the previous fragment was received, the connection is terminated. A value of 0 means no timeout. value: 30000 + BLE_L2CAP_COC_MAX_NUM: + description: > + Defines maximum number of LE Connection Oriented Channels channels. + When set to (0), LE COC is not compiled in. + value: 1 # Security manager settings. BLE_SM_LEGACY: -- 2.9.3