diff --git a/nimble/host/include/host/ble_l2cap.h b/nimble/host/include/host/ble_l2cap.h index 2f7c6a609..aef9682cc 100644 --- a/nimble/host/include/host/ble_l2cap.h +++ b/nimble/host/include/host/ble_l2cap.h @@ -54,7 +54,11 @@ struct ble_hs_conn; #define BLE_L2CAP_SIG_OP_LE_CREDIT_CONNECT_REQ 0x14 #define BLE_L2CAP_SIG_OP_LE_CREDIT_CONNECT_RSP 0x15 #define BLE_L2CAP_SIG_OP_FLOW_CTRL_CREDIT 0x16 -#define BLE_L2CAP_SIG_OP_MAX 0x17 +#define BLE_L2CAP_SIG_OP_CREDIT_CONNECT_REQ 0x17 +#define BLE_L2CAP_SIG_OP_CREDIT_CONNECT_RSP 0x18 +#define BLE_L2CAP_SIG_OP_CREDIT_RECONFIG_REQ 0x19 +#define BLE_L2CAP_SIG_OP_CREDIT_RECONFIG_RSP 0x1A +#define BLE_L2CAP_SIG_OP_MAX 0x1B #define BLE_L2CAP_SIG_ERR_CMD_NOT_UNDERSTOOD 0x0000 #define BLE_L2CAP_SIG_ERR_MTU_EXCEEDED 0x0001 @@ -70,12 +74,21 @@ struct ble_hs_conn; #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 +#define BLE_L2CAP_COC_ERR_INVALID_PARAMETERS 0x000C + +#define BLE_L2CAP_ERR_RECONFIG_SUCCEED 0x0000 +#define BLE_L2CAP_ERR_RECONFIG_REDUCTION_MTU_NOT_ALLOWED 0x0001 +#define BLE_L2CAP_ERR_RECONFIG_REDUCTION_MPS_NOT_ALLOWED 0x0002 +#define BLE_L2CAP_ERR_RECONFIG_INVALID_DCID 0x0003 +#define BLE_L2CAP_ERR_RECONFIG_UNACCAPTED_PARAM 0x0004 #define BLE_L2CAP_EVENT_COC_CONNECTED 0 #define BLE_L2CAP_EVENT_COC_DISCONNECTED 1 #define BLE_L2CAP_EVENT_COC_ACCEPT 2 #define BLE_L2CAP_EVENT_COC_DATA_RECEIVED 3 #define BLE_L2CAP_EVENT_COC_TX_UNSTALLED 4 +#define BLE_L2CAP_EVENT_COC_RECONFIG_COMPLETED 5 +#define BLE_L2CAP_EVENT_COC_PEER_RECONFIGURED 6 typedef void ble_l2cap_sig_update_fn(uint16_t conn_handle, int status, void *arg); @@ -196,6 +209,28 @@ struct ble_l2cap_event { */ int status; } tx_unstalled; + + /** + * Represents reconfiguration done. Valid for the following event + * types: + * o BLE_L2CAP_EVENT_COC_RECONFIG_COMPLETED + * o BLE_L2CAP_EVENT_COC_PEER_RECONFIGURED + */ + struct { + /** + * The status of the reconfiguration attempt; + * o 0: the reconfiguration was successfully done. + * o BLE host error code: the reconfiguration attempt failed for + * the specified reason. + */ + int status; + + /** Connection handle of the relevant connection */ + uint16_t conn_handle; + + /** The L2CAP channel of the relevant L2CAP connection. */ + struct ble_l2cap_chan *chan; + } reconfigured; }; }; diff --git a/nimble/host/src/ble_hs_conn.c b/nimble/host/src/ble_hs_conn.c index fa75a092f..f7edb6240 100644 --- a/nimble/host/src/ble_hs_conn.c +++ b/nimble/host/src/ble_hs_conn.c @@ -88,6 +88,24 @@ ble_hs_conn_chan_find_by_dcid(struct ble_hs_conn *conn, uint16_t cid) return NULL; } +bool +ble_hs_conn_chan_exist(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan) +{ +#if !NIMBLE_BLE_CONNECT + return NULL; +#endif + + struct ble_l2cap_chan *tmp; + + SLIST_FOREACH(tmp, &conn->bhc_channels, next) { + if (chan == tmp) { + return true; + } + } + + return false; +} + int ble_hs_conn_chan_insert(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan) { diff --git a/nimble/host/src/ble_hs_conn_priv.h b/nimble/host/src/ble_hs_conn_priv.h index 757814524..0e451194d 100644 --- a/nimble/host/src/ble_hs_conn_priv.h +++ b/nimble/host/src/ble_hs_conn_priv.h @@ -126,6 +126,7 @@ struct ble_l2cap_chan *ble_hs_conn_chan_find_by_scid(struct ble_hs_conn *conn, uint16_t cid); struct ble_l2cap_chan *ble_hs_conn_chan_find_by_dcid(struct ble_hs_conn *conn, uint16_t cid); +bool ble_hs_conn_chan_exist(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan); int ble_hs_conn_chan_insert(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan); void ble_hs_conn_delete_chan(struct ble_hs_conn *conn, diff --git a/nimble/host/src/ble_l2cap.c b/nimble/host/src/ble_l2cap.c index 19cf8c7d8..0d9f082d4 100644 --- a/nimble/host/src/ble_l2cap.c +++ b/nimble/host/src/ble_l2cap.c @@ -177,6 +177,38 @@ ble_l2cap_get_chan_info(struct ble_l2cap_chan *chan, struct ble_l2cap_chan_info return 0; } +int +ble_l2cap_enhanced_connect(uint16_t conn_handle, + uint16_t psm, uint16_t mtu, + uint8_t num, struct os_mbuf *sdu_rx[], + ble_l2cap_event_fn *cb, void *cb_arg) +{ + return ble_l2cap_sig_ecoc_connect(conn_handle, psm, mtu, + num, sdu_rx, cb, cb_arg); +} + +int +ble_l2cap_reconfig(struct ble_l2cap_chan *chans[], uint8_t num, uint16_t new_mtu) +{ + int i; + uint16_t conn_handle; + + if (num == 0 || !chans) { + return BLE_HS_EINVAL; + } + + conn_handle = chans[0]->conn_handle; + + for (i = 1; i < num; i++) { + if (conn_handle != chans[i]->conn_handle) { + BLE_HS_LOG(ERROR, "All channels should have same conn handle\n"); + return BLE_HS_EINVAL; + } + } + + return ble_l2cap_sig_coc_reconfig(conn_handle, chans, num, new_mtu); +} + int ble_l2cap_disconnect(struct ble_l2cap_chan *chan) { diff --git a/nimble/host/src/ble_l2cap_coc.c b/nimble/host/src/ble_l2cap_coc.c index 798b0dc6e..41a831565 100644 --- a/nimble/host/src/ble_l2cap_coc.c +++ b/nimble/host/src/ble_l2cap_coc.c @@ -274,6 +274,17 @@ ble_l2cap_coc_rx_fn(struct ble_l2cap_chan *chan) return 0; } +void +ble_l2cap_coc_set_new_mtu_mps(struct ble_l2cap_chan *chan, uint16_t mtu, uint16_t mps) +{ + chan->my_coc_mps = mps; + chan->coc_rx.mtu = mtu; + chan->initial_credits = mtu / chan->my_coc_mps; + if (mtu % chan->my_coc_mps) { + chan->initial_credits++; + } +} + struct ble_l2cap_chan * ble_l2cap_coc_chan_alloc(struct ble_hs_conn *conn, uint16_t psm, uint16_t mtu, struct os_mbuf *sdu_rx, ble_l2cap_event_fn *cb, diff --git a/nimble/host/src/ble_l2cap_coc_priv.h b/nimble/host/src/ble_l2cap_coc_priv.h index f8d485003..5ebdaa050 100644 --- a/nimble/host/src/ble_l2cap_coc_priv.h +++ b/nimble/host/src/ble_l2cap_coc_priv.h @@ -70,6 +70,7 @@ void ble_l2cap_coc_le_credits_update(uint16_t conn_handle, uint16_t dcid, int ble_l2cap_coc_recv_ready(struct ble_l2cap_chan *chan, struct os_mbuf *sdu_rx); int ble_l2cap_coc_send(struct ble_l2cap_chan *chan, struct os_mbuf *sdu_tx); +void ble_l2cap_coc_set_new_mtu_mps(struct ble_l2cap_chan *chan, uint16_t mtu, uint16_t mps); #else static inline int ble_l2cap_coc_init(void) { diff --git a/nimble/host/src/ble_l2cap_priv.h b/nimble/host/src/ble_l2cap_priv.h index 937dbaf9e..e3409743b 100644 --- a/nimble/host/src/ble_l2cap_priv.h +++ b/nimble/host/src/ble_l2cap_priv.h @@ -130,6 +130,13 @@ void ble_l2cap_remove_rx(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan); int ble_l2cap_init(void); +/* Below experimental API is available when BLE_VERSION >= 52 */ +int ble_l2cap_enhanced_connect(uint16_t conn_handle, + uint16_t psm, uint16_t mtu, + uint8_t num, struct os_mbuf *sdu_rx[], + ble_l2cap_event_fn *cb, void *cb_arg); +int ble_l2cap_reconfig(struct ble_l2cap_chan *chans[], uint8_t num, uint16_t new_mtu); + #ifdef __cplusplus } #endif diff --git a/nimble/host/src/ble_l2cap_sig.c b/nimble/host/src/ble_l2cap_sig.c index b8beefeab..65c826c47 100644 --- a/nimble/host/src/ble_l2cap_sig.c +++ b/nimble/host/src/ble_l2cap_sig.c @@ -56,8 +56,17 @@ #define BLE_L2CAP_SIG_PROC_OP_UPDATE 0 #define BLE_L2CAP_SIG_PROC_OP_CONNECT 1 -#define BLE_L2CAP_SIG_PROC_OP_DISCONNECT 2 -#define BLE_L2CAP_SIG_PROC_OP_MAX 3 +#define BLE_L2CAP_SIG_PROC_OP_RECONFIG 2 +#define BLE_L2CAP_SIG_PROC_OP_DISCONNECT 3 +#define BLE_L2CAP_SIG_PROC_OP_MAX 4 + +#if MYNEWT_VAL(BLE_L2CAP_ENHANCED_COC) +#define BLE_L2CAP_ECOC_MIN_MTU (64) + +#define BLE_L2CAP_MAX_COC_CONN_REQ (5) +#else +#define BLE_L2CAP_MAX_COC_CONN_REQ (1) +#endif struct ble_l2cap_sig_proc { STAILQ_ENTRY(ble_l2cap_sig_proc) next; @@ -73,11 +82,20 @@ struct ble_l2cap_sig_proc { void *cb_arg; } update; struct { - struct ble_l2cap_chan *chan; + uint8_t chan_cnt; + struct ble_l2cap_chan *chan[BLE_L2CAP_MAX_COC_CONN_REQ]; } connect; struct { struct ble_l2cap_chan *chan; } disconnect; +#if MYNEWT_VAL(BLE_L2CAP_ENHANCED_COC) + struct { + uint8_t cid_cnt; + uint16_t cids[BLE_L2CAP_MAX_COC_CONN_REQ]; + uint16_t new_mps; + uint16_t new_mtu; + } reconfig; +#endif }; }; @@ -105,7 +123,19 @@ static ble_l2cap_sig_rx_fn ble_l2cap_sig_le_credits_rx; #define ble_l2cap_sig_coc_rsp_rx ble_l2cap_sig_rx_noop #define ble_l2cap_sig_disc_rsp_rx ble_l2cap_sig_rx_noop #define ble_l2cap_sig_disc_req_rx ble_l2cap_sig_rx_noop -#define ble_l2cap_sig_le_credits_rx ble_l2cap_sig_rx_noop +#define ble_l2cap_sig_le_credits_rx ble_l2cap_sig_rx_noop +#endif + +#if MYNEWT_VAL(BLE_L2CAP_ENHANCED_COC) +static ble_l2cap_sig_rx_fn ble_l2cap_sig_credit_base_con_req_rx; +static ble_l2cap_sig_rx_fn ble_l2cap_sig_credit_base_con_rsp_rx; +static ble_l2cap_sig_rx_fn ble_l2cap_sig_credit_base_reconfig_req_rx; +static ble_l2cap_sig_rx_fn ble_l2cap_sig_credit_base_reconfig_rsp_rx; +#else +#define ble_l2cap_sig_credit_base_con_req_rx ble_l2cap_sig_rx_noop +#define ble_l2cap_sig_credit_base_con_rsp_rx ble_l2cap_sig_rx_noop +#define ble_l2cap_sig_credit_base_reconfig_req_rx ble_l2cap_sig_rx_noop +#define ble_l2cap_sig_credit_base_reconfig_rsp_rx ble_l2cap_sig_rx_noop #endif static ble_l2cap_sig_rx_fn * const ble_l2cap_sig_dispatch[] = { @@ -124,6 +154,10 @@ static ble_l2cap_sig_rx_fn * const ble_l2cap_sig_dispatch[] = { [BLE_L2CAP_SIG_OP_LE_CREDIT_CONNECT_REQ] = ble_l2cap_sig_coc_req_rx, [BLE_L2CAP_SIG_OP_LE_CREDIT_CONNECT_RSP] = ble_l2cap_sig_coc_rsp_rx, [BLE_L2CAP_SIG_OP_FLOW_CTRL_CREDIT] = ble_l2cap_sig_le_credits_rx, + [BLE_L2CAP_SIG_OP_CREDIT_CONNECT_REQ] = ble_l2cap_sig_credit_base_con_req_rx, + [BLE_L2CAP_SIG_OP_CREDIT_CONNECT_RSP] = ble_l2cap_sig_credit_base_con_rsp_rx, + [BLE_L2CAP_SIG_OP_CREDIT_RECONFIG_REQ] = ble_l2cap_sig_credit_base_reconfig_req_rx, + [BLE_L2CAP_SIG_OP_CREDIT_RECONFIG_RSP] = ble_l2cap_sig_credit_base_reconfig_rsp_rx, }; static uint8_t ble_l2cap_sig_cur_id; @@ -609,31 +643,457 @@ ble_l2cap_sig_coc_connect_cb(struct ble_l2cap_sig_proc *proc, int status) { struct ble_hs_conn *conn; struct ble_l2cap_chan *chan; + int i; + bool some_not_connected = false; if (!proc) { return; } - chan = proc->connect.chan; - if (!chan || !chan->cb) { + for (i = 0; i < proc->connect.chan_cnt; i++) { + chan = proc->connect.chan[i]; + if (!chan || !chan->cb) { + continue; + } + + if ((status == 0) && (chan->dcid != 0)) { + ble_l2cap_event_coc_connected(chan, status); + /* Let's forget about connected channel now. + * Not connected will be freed later on. + */ + proc->connect.chan[i] = NULL; + continue; + } + some_not_connected = true; + ble_l2cap_event_coc_connected(chan, status ? status : BLE_HS_EREJECT); + } + + if (!some_not_connected) { return; } - ble_l2cap_event_coc_connected(chan, status); + /* Free not connected channels*/ - if (status) { - /* Normally in channel free we send disconnected event to application. - * However in case on error during creation connection we send connected - * event with error status. To avoid additional disconnected event lets - * clear callbacks since we don't needed it anymore.*/ - chan->cb = NULL; - ble_hs_lock(); - conn = ble_hs_conn_find(chan->conn_handle); - ble_l2cap_chan_free(conn, chan); + ble_hs_lock(); + conn = ble_hs_conn_find(chan->conn_handle); + for (i = 0; i < proc->connect.chan_cnt; i++) { + chan = proc->connect.chan[i]; + if (chan) { + /* Normally in channel free we send disconnected event to application. + * However in case on error during creation connection we send connected + * event with error status. To avoid additional disconnected event lets + * clear callbacks since we don't needed it anymore. + */ + chan->cb = NULL; + ble_l2cap_chan_free(conn, chan); + } + } + ble_hs_unlock(); +} + +#if MYNEWT_VAL(BLE_L2CAP_ENHANCED_COC) +static void +ble_l2cap_event_coc_reconfigured(uint16_t conn_handle, uint16_t status, + struct ble_l2cap_chan *chan, bool peer) +{ + struct ble_l2cap_event event = { }; + + if (peer) { + event.type = BLE_L2CAP_EVENT_COC_PEER_RECONFIGURED; + } else { + event.type = BLE_L2CAP_EVENT_COC_RECONFIG_COMPLETED; + } + event.reconfigured.conn_handle = conn_handle; + event.reconfigured.chan = chan; + event.reconfigured.status = status; + + chan->cb(&event, chan->cb_arg); +} + +static int +ble_l2cap_sig_credit_base_reconfig_req_rx(uint16_t conn_handle, + struct ble_l2cap_sig_hdr *hdr, + struct os_mbuf **om) +{ + struct ble_l2cap_chan *chan[BLE_L2CAP_MAX_COC_CONN_REQ] = {0}; + struct ble_l2cap_sig_credit_base_reconfig_req *req; + struct ble_l2cap_sig_credit_base_reconfig_rsp *rsp; + struct ble_hs_conn *conn; + struct os_mbuf *txom; + int i; + int rc; + uint8_t cid_cnt; + uint8_t reduction_mps = 0; + + rc = ble_hs_mbuf_pullup_base(om, hdr->length); + if (rc != 0) { + return rc; + } + + ble_hs_lock(); + conn = ble_hs_conn_find(conn_handle); + if (!conn) { ble_hs_unlock(); + return 0; + } + + rsp = ble_l2cap_sig_cmd_get(BLE_L2CAP_SIG_OP_CREDIT_RECONFIG_RSP, + hdr->identifier, sizeof(*rsp) , &txom); + if (!rsp) { + /* TODO: Reuse request buffer for the response. For now in such a case + * remote will timeout. + */ + BLE_HS_LOG(ERROR, "No memory for the response\n"); + ble_hs_unlock(); + return 0; + } + + if (hdr->length <= sizeof(*req)) { + rsp->result = htole16(BLE_L2CAP_ERR_RECONFIG_UNACCAPTED_PARAM); + goto done; + } + + req = (struct ble_l2cap_sig_credit_base_reconfig_req *)(*om)->om_data; + + if ((req->mps < BLE_L2CAP_ECOC_MIN_MTU) || (req->mtu < BLE_L2CAP_ECOC_MIN_MTU)) { + rsp->result = htole16(BLE_L2CAP_ERR_RECONFIG_UNACCAPTED_PARAM); + goto done; + } + + /* Assume request will succeed. If not, result will be updated */ + rsp->result = htole16(BLE_L2CAP_ERR_RECONFIG_SUCCEED); + + cid_cnt = (hdr->length - sizeof(*req)) / sizeof(uint16_t); + if (cid_cnt > BLE_L2CAP_MAX_COC_CONN_REQ) { + rsp->result = htole16(BLE_L2CAP_ERR_RECONFIG_UNACCAPTED_PARAM); + goto done; + } + + for (i = 0; i < cid_cnt; i++) { + chan[i] = ble_hs_conn_chan_find_by_dcid(conn, req->dcids[i]); + if (!chan[i]) { + rsp->result = htole16(BLE_L2CAP_ERR_RECONFIG_INVALID_DCID); + ble_hs_unlock(); + goto done; + } + + if (chan[i]->peer_coc_mps > req->mps) { + reduction_mps++; + if (reduction_mps > 1) { + rsp->result = htole16(BLE_L2CAP_ERR_RECONFIG_REDUCTION_MPS_NOT_ALLOWED); + ble_hs_unlock(); + goto done; + } + } + + if (chan[i]->coc_tx.mtu > req->mtu) { + rsp->result = htole16(BLE_L2CAP_ERR_RECONFIG_REDUCTION_MTU_NOT_ALLOWED); + ble_hs_unlock(); + goto done; + } + } + + for (i = 0; i < cid_cnt; i++) { + chan[i]->coc_tx.mtu = req->mtu; + chan[i]->peer_coc_mps = req->mps; + ble_l2cap_event_coc_reconfigured(conn_handle, 0, chan[i], true); + } + + ble_hs_unlock(); +done: + ble_l2cap_sig_tx(conn_handle, txom); + return 0; +} + +static void +ble_l2cap_sig_coc_reconfig_cb(struct ble_l2cap_sig_proc *proc, int status) +{ + int i; + struct ble_l2cap_chan *chan[BLE_L2CAP_MAX_COC_CONN_REQ] = {0}; + struct ble_hs_conn *conn; + + ble_hs_lock(); + + conn = ble_hs_conn_find(proc->conn_handle); + if (!conn) { + ble_hs_unlock(); + return; + } + + for (i = 0; i< proc->reconfig.cid_cnt; i++) { + chan[i] = ble_hs_conn_chan_find_by_scid(conn, proc->reconfig.cids[i]); + if (status == 0) { + ble_l2cap_coc_set_new_mtu_mps(chan[i], proc->reconfig.new_mtu, proc->reconfig.new_mps); + } + } + + ble_hs_unlock(); + + for (i = 0; i < proc->reconfig.cid_cnt; i++) { + ble_l2cap_event_coc_reconfigured(proc->conn_handle, status, chan[i], false); } } +static int +ble_l2cap_sig_credit_base_reconfig_rsp_rx(uint16_t conn_handle, + struct ble_l2cap_sig_hdr *hdr, + struct os_mbuf **om) +{ + struct ble_l2cap_sig_proc *proc; + struct ble_l2cap_sig_credit_base_reconfig_rsp *rsp; + int rc; + + proc = ble_l2cap_sig_proc_extract(conn_handle, + BLE_L2CAP_SIG_PROC_OP_RECONFIG, + hdr->identifier); + if (!proc) { + return 0; + } + + rc = ble_hs_mbuf_pullup_base(om, hdr->length); + if (rc != 0) { + return rc; + } + + rsp = (struct ble_l2cap_sig_credit_base_reconfig_rsp *)(*om)->om_data; + ble_l2cap_sig_coc_reconfig_cb(proc, (rsp->result > 0) ? BLE_HS_EREJECT : 0); + + return 0; +} + +static int +ble_l2cap_sig_credit_base_con_req_rx(uint16_t conn_handle, + struct ble_l2cap_sig_hdr *hdr, + struct os_mbuf **om) +{ + int rc; + struct ble_l2cap_sig_credit_base_connect_req *req; + struct os_mbuf *txom; + struct ble_l2cap_sig_credit_base_connect_rsp *rsp; + struct ble_l2cap_chan *chans[5] = { 0 }; + struct ble_hs_conn *conn; + uint16_t scid; + uint8_t num_of_scids; + uint8_t chan_created = 0; + int i; + uint8_t len; + + rc = ble_hs_mbuf_pullup_base(om, hdr->length); + if (rc != 0) { + return rc; + } + + len = (hdr->length > sizeof(*req)) ? hdr->length : sizeof(*req); + + rsp = ble_l2cap_sig_cmd_get(BLE_L2CAP_SIG_OP_CREDIT_CONNECT_RSP, + hdr->identifier, len , &txom); + if (!rsp) { + /* Well, nothing smart we can do if there is no memory for response. + * Remote will timeout. + */ + return 0; + } + + ble_hs_lock(); + + memset(rsp, 0, len); + + /* Initial dummy values in case of error, just to satisfy PTS */ + rsp->credits = htole16(1); + rsp->mps = htole16(BLE_L2CAP_ECOC_MIN_MTU); + rsp->mtu = htole16(BLE_L2CAP_ECOC_MIN_MTU); + + if (hdr->length <= sizeof(*req)) { + rsp->result = htole16(BLE_L2CAP_COC_ERR_INVALID_PARAMETERS); + goto failed; + } + + req = (struct ble_l2cap_sig_credit_base_connect_req *)(*om)->om_data; + + num_of_scids = (hdr->length - sizeof(*req)) / sizeof(uint16_t); + if (num_of_scids > 5) { + rsp->result = htole16(BLE_L2CAP_COC_ERR_INVALID_PARAMETERS); + goto failed; + } + + if ((req->mtu < BLE_L2CAP_ECOC_MIN_MTU) || (req->mps < BLE_L2CAP_ECOC_MIN_MTU)) { + rsp->result = htole16(BLE_L2CAP_COC_ERR_INVALID_PARAMETERS); + goto failed; + } + + conn = ble_hs_conn_find_assert(conn_handle); + + /* First verify that provided SCIDs are good */ + for (i = 0; i < num_of_scids; i++) { + scid = le16toh(req->scids[i]); + if (scid < BLE_L2CAP_COC_CID_START || scid > BLE_L2CAP_COC_CID_END) { + rsp->result = htole16(BLE_L2CAP_COC_ERR_INVALID_SOURCE_CID); + goto failed; + } + } + + /* Let us try to connect channels */ + for (i = 0; i < num_of_scids; i++) { + /* Verify CID. Note, scid in the request is dcid for out local channel */ + scid = le16toh(req->scids[i]); + chans[i] = ble_hs_conn_chan_find_by_dcid(conn, scid); + if (chans[i]) { + rsp->result = htole16(BLE_L2CAP_COC_ERR_SOURCE_CID_ALREADY_USED); + rsp->dcids[i] = htole16(chans[i]->scid); + continue; + } + + rc = ble_l2cap_coc_create_srv_chan(conn, le16toh(req->psm), &chans[i]); + if (rc != 0) { + if (i == 0) { + /* In case it is very first channel we cannot create it means PSM is incorrect + * or we are out of resources. Just send a response now. + */ + rsp->result = htole16(ble_l2cap_sig_ble_hs_err2coc_err(rc)); + goto failed; + } else { + /* We cannot create number of channels req by peer due to limited resources. */ + rsp->result = htole16(BLE_L2CAP_COC_ERR_NO_RESOURCES); + goto done; + } + } + + /* Fill up remote configuration. Note MPS is the L2CAP MTU*/ + chans[i]->dcid = scid; + chans[i]->peer_coc_mps = le16toh(req->mps); + chans[i]->coc_tx.credits = le16toh(req->credits); + chans[i]->coc_tx.mtu = le16toh(req->mtu); + + ble_hs_conn_chan_insert(conn, chans[i]); + /* Sending event to the app. Unlock hs */ + ble_hs_unlock(); + + rc = ble_l2cap_event_coc_accept(chans[i], le16toh(req->mtu)); + if (rc == 0) { + rsp->dcids[i] = htole16(chans[i]->scid); + chan_created++; + if (chan_created == 1) { + /* We need to set it once as there are same initial parameters + * for all the channels + */ + rsp->credits = htole16(chans[i]->coc_rx.credits); + rsp->mps = htole16(chans[i]->my_mtu); + rsp->mtu = htole16(chans[i]->coc_rx.mtu); + } + } else { + /* Make sure we do not send disconnect event when removing channel */ + chans[i]->cb = NULL; + + ble_hs_lock(); + conn = ble_hs_conn_find_assert(conn_handle); + ble_hs_conn_delete_chan(conn, chans[i]); + chans[i] = NULL; + rsp->result = htole16(ble_l2cap_sig_ble_hs_err2coc_err(rc)); + rc = 0; + ble_hs_unlock(); + } + + ble_hs_lock(); + conn = ble_hs_conn_find_assert(conn_handle); + } + +done: + ble_hs_unlock(); + rc = ble_l2cap_sig_tx(conn_handle, txom); + if (rc != 0) { + ble_hs_lock(); + conn = ble_hs_conn_find_assert(conn_handle); + for (i = 0; i < num_of_scids; i++) { + if (chans[i]) { + ble_hs_conn_delete_chan(conn, chans[i]); + } + } + ble_hs_unlock(); + return 0; + } + + /* Notify user about connection status */ + for (i = 0; i < num_of_scids; i++) { + if (chans[i]) { + ble_l2cap_event_coc_connected(chans[i], rc); + } + } + + return 0; + +failed: + ble_hs_unlock(); + ble_l2cap_sig_tx(conn_handle, txom); + return 0; +} + +static int +ble_l2cap_sig_credit_base_con_rsp_rx(uint16_t conn_handle, + struct ble_l2cap_sig_hdr *hdr, + struct os_mbuf **om) +{ + struct ble_l2cap_sig_proc *proc; + struct ble_l2cap_sig_credit_base_connect_rsp *rsp; + struct ble_l2cap_chan *chan; + struct ble_hs_conn *conn; + int rc; + int i; + +#if !BLE_MONITOR + BLE_HS_LOG(DEBUG, "L2CAP LE COC connection response received\n"); +#endif + + proc = ble_l2cap_sig_proc_extract(conn_handle, + BLE_L2CAP_SIG_PROC_OP_CONNECT, + hdr->identifier); + if (!proc) { + return 0; + } + + rc = ble_hs_mbuf_pullup_base(om, hdr->length); + if (rc != 0) { + goto done; + } + + rsp = (struct ble_l2cap_sig_credit_base_connect_rsp *)(*om)->om_data; + + if (rsp->result) { + rc = ble_l2cap_sig_coc_err2ble_hs_err(le16toh(rsp->result)); + goto done; + } + + ble_hs_lock(); + conn = ble_hs_conn_find(conn_handle); + assert(conn != NULL); + + for (i = 0; i < proc->connect.chan_cnt; i++) { + chan = proc->connect.chan[i]; + if (rsp->dcids[i] == 0) { + /* Channel rejected, dont put it on the list. + * User will get notified later in that function + */ + chan->dcid = 0; + continue; + } + chan->peer_coc_mps = le16toh(rsp->mps); + chan->dcid = le16toh(rsp->dcids[i]); + chan->coc_tx.mtu = le16toh(rsp->mtu); + chan->coc_tx.credits = le16toh(rsp->credits); + + ble_hs_conn_chan_insert(conn, chan); + } + + ble_hs_unlock(); + +done: + ble_l2cap_sig_coc_connect_cb(proc, rc); + ble_l2cap_sig_proc_free(proc); + + /* Silently ignore errors as this is response signal */ + return 0; +} +#endif + static int ble_l2cap_sig_coc_req_rx(uint16_t conn_handle, struct ble_l2cap_sig_hdr *hdr, struct os_mbuf **om) @@ -767,7 +1227,7 @@ ble_l2cap_sig_coc_rsp_rx(uint16_t conn_handle, struct ble_l2cap_sig_hdr *hdr, rsp = (struct ble_l2cap_sig_le_con_rsp *)(*om)->om_data; - chan = proc->connect.chan; + chan = proc->connect.chan[0]; if (rsp->result) { rc = ble_l2cap_sig_coc_err2ble_hs_err(le16toh(rsp->result)); @@ -838,7 +1298,8 @@ ble_l2cap_sig_coc_connect(uint16_t conn_handle, uint16_t psm, uint16_t mtu, proc->op = BLE_L2CAP_SIG_PROC_OP_CONNECT; proc->id = ble_l2cap_sig_next_id(); proc->conn_handle = conn_handle; - proc->connect.chan = chan; + proc->connect.chan[0] = chan; + proc->connect.chan_cnt = 1; req = ble_l2cap_sig_cmd_get(BLE_L2CAP_SIG_OP_LE_CREDIT_CONNECT_REQ, proc->id, sizeof(*req), &txom); @@ -869,6 +1330,158 @@ ble_l2cap_sig_coc_connect(uint16_t conn_handle, uint16_t psm, uint16_t mtu, return rc; } +#if MYNEWT_VAL(BLE_L2CAP_ENHANCED_COC) +int +ble_l2cap_sig_ecoc_connect(uint16_t conn_handle, uint16_t psm, uint16_t mtu, + uint8_t num, struct os_mbuf *sdu_rx[], + ble_l2cap_event_fn *cb, void *cb_arg) +{ + struct ble_hs_conn *conn; + struct ble_l2cap_sig_proc *proc; + struct ble_l2cap_chan *chan = NULL; + struct os_mbuf *txom; + struct ble_l2cap_sig_credit_base_connect_req *req; + int rc; + int i; + int j; + + if (!sdu_rx || !cb) { + return BLE_HS_EINVAL; + } + + ble_hs_lock(); + conn = ble_hs_conn_find(conn_handle); + + if (!conn) { + ble_hs_unlock(); + return BLE_HS_ENOTCONN; + } + + proc = ble_l2cap_sig_proc_alloc(); + if (!proc) { + ble_hs_unlock(); + return BLE_HS_ENOMEM; + } + + req = ble_l2cap_sig_cmd_get(BLE_L2CAP_SIG_OP_CREDIT_CONNECT_REQ, proc->id, + sizeof(*req) + num * sizeof(uint16_t), &txom); + if (!req) { + ble_hs_unlock(); + rc = BLE_HS_ENOMEM; + /* Goto done to clear proc */ + goto done; + } + + for (i = 0; i < num; i++) { + chan = ble_l2cap_coc_chan_alloc(conn, psm, mtu, sdu_rx[i], cb, cb_arg); + if (!chan) { + /* Clear request buffer */ + os_mbuf_free_chain(txom); + + for (j = 0; j < i; j++) { + /* Clear callback to make sure "Disconnected event" to the user */ + chan[j].cb = NULL; + ble_l2cap_chan_free(conn, proc->connect.chan[j]); + } + ble_hs_unlock(); + rc = BLE_HS_ENOMEM; + goto done; + } + proc->connect.chan[i] = chan; + } + proc->connect.chan_cnt = num; + + proc->op = BLE_L2CAP_SIG_PROC_OP_CONNECT; + proc->id = ble_l2cap_sig_next_id(); + proc->conn_handle = conn_handle; + + req->psm = htole16(psm); + req->mtu = htole16(chan->coc_rx.mtu); + req->mps = htole16(chan->my_mtu); + req->credits = htole16(chan->coc_rx.credits); + for (i = 0; i < num; i++) { + req->scids[i] = htole16(proc->connect.chan[i]->scid); + } + + ble_hs_unlock(); + + rc = ble_l2cap_sig_tx(proc->conn_handle, txom); + +done: + ble_l2cap_sig_process_status(proc, rc); + + return rc; +} + +int +ble_l2cap_sig_coc_reconfig(uint16_t conn_handle, struct ble_l2cap_chan *chans[], + uint8_t num, uint16_t new_mtu) +{ + struct ble_hs_conn *conn; + struct ble_l2cap_sig_proc *proc; + struct os_mbuf *txom; + struct ble_l2cap_sig_credit_base_reconfig_req *req; + int rc; + int i; + + ble_hs_lock(); + conn = ble_hs_conn_find(conn_handle); + + if (!conn) { + ble_hs_unlock(); + return BLE_HS_ENOTCONN; + } + + proc = ble_l2cap_sig_proc_alloc(); + if (!proc) { + ble_hs_unlock(); + return BLE_HS_ENOMEM; + } + + for (i = 0; i < num; i++) { + if (ble_hs_conn_chan_exist(conn, chans[i])) { + proc->reconfig.cids[i] = chans[i]->scid; + } else { + ble_hs_unlock(); + rc = BLE_HS_ENOMEM; + goto done; + } + } + + proc->op = BLE_L2CAP_SIG_PROC_OP_RECONFIG; + proc->reconfig.cid_cnt = num; + proc->reconfig.new_mtu = new_mtu; + proc->reconfig.new_mps = MYNEWT_VAL(BLE_L2CAP_COC_MPS); + proc->id = ble_l2cap_sig_next_id(); + proc->conn_handle = conn_handle; + + req = ble_l2cap_sig_cmd_get(BLE_L2CAP_SIG_OP_CREDIT_RECONFIG_REQ, proc->id, + sizeof(*req) + num * sizeof(uint16_t), &txom); + if (!req) { + ble_hs_unlock(); + rc = BLE_HS_ENOMEM; + goto done; + } + + /* For now we allow to change CoC MTU only.*/ + req->mtu = htole16(proc->reconfig.new_mtu); + req->mps = htole16(proc->reconfig.new_mps); + + for (i = 0; i < num; i++) { + req->dcids[i] = htole16(proc->reconfig.cids[i]); + } + + ble_hs_unlock(); + + rc = ble_l2cap_sig_tx(proc->conn_handle, txom); + +done: + ble_l2cap_sig_process_status(proc, rc); + + return rc; +} +#endif + /***************************************************************************** * $disconnect * *****************************************************************************/ @@ -1246,6 +1859,11 @@ ble_l2cap_sig_conn_broken(uint16_t conn_handle, int reason) case BLE_L2CAP_SIG_PROC_OP_DISCONNECT: ble_l2cap_sig_coc_disconnect_cb(proc, reason); break; +#if MYNEWT_VAL(BLE_L2CAP_ENHANCED_COC) + case BLE_L2CAP_SIG_PROC_OP_RECONFIG: + ble_l2cap_sig_coc_reconfig_cb(proc, reason); + break; +#endif #endif } diff --git a/nimble/host/src/ble_l2cap_sig_priv.h b/nimble/host/src/ble_l2cap_sig_priv.h index 462a9e434..a698cd0d8 100644 --- a/nimble/host/src/ble_l2cap_sig_priv.h +++ b/nimble/host/src/ble_l2cap_sig_priv.h @@ -74,6 +74,32 @@ struct ble_l2cap_sig_le_con_rsp { uint16_t result; } __attribute__((packed)); +struct ble_l2cap_sig_credit_base_connect_req { + uint16_t psm; + uint16_t mtu; + uint16_t mps; + uint16_t credits; + uint16_t scids[0]; +} __attribute__((packed)); + +struct ble_l2cap_sig_credit_base_connect_rsp { + uint16_t mtu; + uint16_t mps; + uint16_t credits; + uint16_t result; + uint16_t dcids[0]; +} __attribute__((packed)); + +struct ble_l2cap_sig_credit_base_reconfig_req { + uint16_t mtu; + uint16_t mps; + uint16_t dcids[0]; +} __attribute__((packed)); + +struct ble_l2cap_sig_credit_base_reconfig_rsp { + uint16_t result; +} __attribute__((packed)); + struct ble_l2cap_sig_disc_req { uint16_t dcid; uint16_t scid; @@ -122,6 +148,30 @@ ble_l2cap_sig_disconnect(struct ble_l2cap_chan *chan) } #endif +#if MYNEWT_VAL(BLE_L2CAP_ENHANCED_COC) +int ble_l2cap_sig_ecoc_connect(uint16_t conn_handle, + uint16_t psm, uint16_t mtu, + uint8_t num, struct os_mbuf *sdu_rx[], + ble_l2cap_event_fn *cb, void *cb_arg); +int ble_l2cap_sig_coc_reconfig(uint16_t conn_handle, struct ble_l2cap_chan *chans[], + uint8_t num, uint16_t new_mtu); +#else +static inline int +ble_l2cap_sig_ecoc_connect(uint16_t conn_handle, + uint16_t psm, uint16_t mtu, + uint8_t num, struct os_mbuf *sdu_rx[], + ble_l2cap_event_fn *cb, void *cb_arg) +{ + return BLE_HS_ENOTSUP; +} +static inline int +ble_l2cap_sig_coc_reconfig(uint16_t conn_handle, struct ble_l2cap_chan *chans[], + uint8_t num, uint16_t new_mtu) +{ + return BLE_HS_ENOTSUP; +} +#endif + void ble_l2cap_sig_conn_broken(uint16_t conn_handle, int reason); int32_t ble_l2cap_sig_timer(void); struct ble_l2cap_chan *ble_l2cap_sig_create_chan(uint16_t conn_handle); diff --git a/nimble/host/syscfg.yml b/nimble/host/syscfg.yml index 7e6eb449e..e72e8d529 100644 --- a/nimble/host/syscfg.yml +++ b/nimble/host/syscfg.yml @@ -118,6 +118,13 @@ syscfg.defs: MSYS blocks. value: 'MYNEWT_VAL_MSYS_1_BLOCK_SIZE-8' + BLE_L2CAP_ENHANCED_COC: + description: > + Enables LE Enhanced CoC mode. + value: 0 + restrictions: + - '(BLE_L2CAP_COC_MAX_NUM > 0) && (BLE_VERSION >= 52) if 1' + # Security manager settings. BLE_SM_LEGACY: description: 'Security manager legacy pairing.' diff --git a/nimble/host/test/src/ble_l2cap_test.c b/nimble/host/test/src/ble_l2cap_test.c index aa5e2a43e..95523d806 100644 --- a/nimble/host/test/src/ble_l2cap_test.c +++ b/nimble/host/test/src/ble_l2cap_test.c @@ -704,7 +704,8 @@ struct test_data { uint16_t event_iter; uint16_t psm; uint16_t mtu; - struct ble_l2cap_chan *chan; + uint8_t num; + struct ble_l2cap_chan *chan[5]; }; static int @@ -719,7 +720,7 @@ ble_l2cap_test_event(struct ble_l2cap_event *event, void *arg) switch(event->type) { case BLE_L2CAP_EVENT_COC_CONNECTED: assert(ev->app_status == event->connect.status); - t->chan = event->connect.chan; + t->chan[0] = event->connect.chan; return 0; case BLE_L2CAP_EVENT_COC_DISCONNECTED: return 0; @@ -759,6 +760,74 @@ static uint16_t ble_l2cap_calculate_credits(uint16_t mtu, uint16_t mps) return credits; } +static void +ble_l2cap_test_coc_connect_multi(struct test_data *t) +{ + struct ble_l2cap_sig_credit_base_connect_req req = {}; + struct ble_l2cap_sig_credit_base_connect_rsp rsp = {}; + struct os_mbuf *sdu_rx[t->num]; + struct event *ev = &t->event[t->event_iter++]; + uint8_t id; + int rc; + int i; + + ble_l2cap_test_util_init(); + + ble_l2cap_test_util_create_conn(2, ((uint8_t[]){1,2,3,4,5,6}), + ble_l2cap_test_util_conn_cb, NULL); + + for (i = 0; i < t->num; i++) { + sdu_rx[i] = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0); + assert(sdu_rx[i] != NULL); + } + + rc = ble_l2cap_sig_ecoc_connect(2, t->psm, t->mtu, t->num, sdu_rx, + ble_l2cap_test_event, t); + TEST_ASSERT_FATAL(rc == ev->early_error); + + if (rc != 0) { + for (i = 0; i< t->num; i++) { + rc = os_mbuf_free_chain(sdu_rx[i]); + TEST_ASSERT_FATAL(rc == 0); + } + + return; + } + + req.credits = htole16( + ble_l2cap_calculate_credits(t->mtu, + MYNEWT_VAL(BLE_L2CAP_COC_MPS))); + req.mps = htole16(MYNEWT_VAL(BLE_L2CAP_COC_MPS)); + req.mtu = htole16(t->mtu); + req.psm = htole16(t->psm); + for (i = 0; i < t->num; i++) { + req.scids[i] = htole16(current_cid + i); + } + + /* Ensure an update request got sent. */ + id = ble_hs_test_util_verify_tx_l2cap_sig( + BLE_L2CAP_SIG_OP_CREDIT_CONNECT_REQ, + &req, sizeof(req) + t->num * sizeof(uint16_t)); + + /* Use some different parameters for peer. Just keep mtu same for testing + * only*/ + rsp.credits = htole16(10); + for (i = 0; i < t->num; i++) { + rsp.dcids[i] = htole16(current_cid + i); + } + rsp.mps = htole16(MYNEWT_VAL(BLE_L2CAP_COC_MPS) + 16); + rsp.mtu = htole16(t->mtu); + rsp.result = htole16(ev->l2cap_status); + + rc = ble_hs_test_util_inject_rx_l2cap_sig(2, + BLE_L2CAP_SIG_OP_CREDIT_CONNECT_RSP, + id, &rsp, sizeof(rsp) + t->num * sizeof(uint16_t)); + TEST_ASSERT(rc == 0); + + /* Ensure callback got called. */ + TEST_ASSERT(ev->handled); +} + static void ble_l2cap_test_coc_connect(struct test_data *t) { @@ -881,11 +950,11 @@ ble_l2cap_test_coc_disc(struct test_data *t) uint8_t id; int rc; - rc = ble_l2cap_sig_disconnect(t->chan); + rc = ble_l2cap_sig_disconnect(t->chan[0]); TEST_ASSERT_FATAL(rc == 0); - req.dcid = htole16(t->chan->dcid); - req.scid = htole16(t->chan->scid); + req.dcid = htole16(t->chan[0]->dcid); + req.scid = htole16(t->chan[0]->scid); /* Ensure an update request got sent. */ id = ble_hs_test_util_verify_tx_l2cap_sig(BLE_L2CAP_SIG_OP_DISCONN_REQ, @@ -910,8 +979,8 @@ ble_l2cap_test_coc_disc_by_peer(struct test_data *t) /* Receive disconnect request from peer. Note that source cid * and destination cid are from peer perspective */ - req.dcid = htole16(t->chan->scid); - req.scid = htole16(t->chan->dcid); + req.dcid = htole16(t->chan[0]->scid); + req.scid = htole16(t->chan[0]->dcid); rc = ble_hs_test_util_inject_rx_l2cap_sig(2, BLE_L2CAP_SIG_OP_DISCONN_REQ, id, &req, sizeof(req)); @@ -937,7 +1006,7 @@ ble_l2cap_test_coc_invalid_disc_by_peer(struct test_data *t) /* Receive disconnect request from peer. Note that source cid * and destination cid are from peer perspective */ - req.dcid = htole16(t->chan->scid); + req.dcid = htole16(t->chan[0]->scid); req.scid = htole16(0); rc = ble_hs_test_util_inject_rx_l2cap_sig(2, BLE_L2CAP_SIG_OP_DISCONN_REQ, @@ -974,7 +1043,7 @@ ble_l2cap_test_coc_send_data(struct test_data *t) rc = os_mbuf_append(sdu_copy, ev->data, ev->data_len); TEST_ASSERT(rc == 0); - rc = ble_l2cap_send(t->chan, sdu); + rc = ble_l2cap_send(t->chan[0], sdu); TEST_ASSERT(rc == ev->early_error); if (rc) { @@ -1017,7 +1086,7 @@ ble_l2cap_test_coc_recv_data(struct test_data *t) assert(sdu != NULL); put_le16(sdu->om_data, ev->data_len); - ble_hs_test_util_inject_rx_l2cap(2, t->chan->scid, sdu); + ble_hs_test_util_inject_rx_l2cap(2, t->chan[0]->scid, sdu); } static void @@ -1369,6 +1438,32 @@ TEST_CASE_SELF(ble_l2cap_test_case_coc_recv_data_succeed) ble_hs_test_util_assert_mbufs_freed(NULL); } +TEST_CASE_SELF(ble_l2cap_test_case_sig_coc_conn_multi) +{ + struct test_data t; + int rc; + + ble_l2cap_test_util_init(); + ble_l2cap_test_set_chan_test_conf(BLE_L2CAP_TEST_PSM, + BLE_L2CAP_TEST_COC_MTU, &t); + t.expected_num_of_ev = 2; + t.num = 2; + + t.event[0].type = BLE_L2CAP_EVENT_COC_CONNECTED; + t.event[1].type = BLE_L2CAP_EVENT_COC_CONNECTED; + + /* Register server */ + rc = ble_l2cap_create_server(t.psm, BLE_L2CAP_TEST_COC_MTU, + ble_l2cap_test_event, &t); + TEST_ASSERT(rc == 0); + + ble_l2cap_test_coc_connect_multi(&t); + + TEST_ASSERT(t.expected_num_of_ev == t.event_cnt); + + ble_hs_test_util_assert_mbufs_freed(NULL); +} + TEST_SUITE(ble_l2cap_test_suite) { ble_l2cap_test_case_bad_header(); @@ -1398,4 +1493,5 @@ TEST_SUITE(ble_l2cap_test_suite) ble_l2cap_test_case_coc_send_data_succeed(); ble_l2cap_test_case_coc_send_data_failed_too_big_sdu(); ble_l2cap_test_case_coc_recv_data_succeed(); + ble_l2cap_test_case_sig_coc_conn_multi(); } diff --git a/nimble/host/test/syscfg.yml b/nimble/host/test/syscfg.yml index ad55f1e74..6307398e4 100644 --- a/nimble/host/test/syscfg.yml +++ b/nimble/host/test/syscfg.yml @@ -25,5 +25,7 @@ syscfg.vals: BLE_SM: 1 BLE_SM_SC: 1 MSYS_1_BLOCK_COUNT: 100 - BLE_L2CAP_COC_MAX_NUM: 1 + BLE_L2CAP_COC_MAX_NUM: 2 CONFIG_FCB: 1 + BLE_VERSION: 52 + BLE_L2CAP_ENHANCED_COC: 1 diff --git a/porting/examples/linux/include/syscfg/syscfg.h b/porting/examples/linux/include/syscfg/syscfg.h index 6e34cb84a..7e9bfcac9 100644 --- a/porting/examples/linux/include/syscfg/syscfg.h +++ b/porting/examples/linux/include/syscfg/syscfg.h @@ -629,6 +629,10 @@ #define MYNEWT_VAL_BLE_L2CAP_COC_MPS (MYNEWT_VAL_MSYS_1_BLOCK_SIZE-8) #endif +#ifndef MYNEWT_VAL_BLE_L2CAP_ENHANCED_COC +#define MYNEWT_VAL_BLE_L2CAP_ENHANCED_COC (0) +#endif + #ifndef MYNEWT_VAL_BLE_L2CAP_JOIN_RX_FRAGS #define MYNEWT_VAL_BLE_L2CAP_JOIN_RX_FRAGS (1) #endif diff --git a/porting/examples/linux_blemesh/include/syscfg/syscfg.h b/porting/examples/linux_blemesh/include/syscfg/syscfg.h index dcacad4be..24ad0c58e 100644 --- a/porting/examples/linux_blemesh/include/syscfg/syscfg.h +++ b/porting/examples/linux_blemesh/include/syscfg/syscfg.h @@ -630,6 +630,10 @@ #define MYNEWT_VAL_BLE_L2CAP_COC_MPS (MYNEWT_VAL_MSYS_1_BLOCK_SIZE-8) #endif +#ifndef MYNEWT_VAL_BLE_L2CAP_ENHANCED_COC +#define MYNEWT_VAL_BLE_L2CAP_ENHANCED_COC (0) +#endif + #ifndef MYNEWT_VAL_BLE_L2CAP_JOIN_RX_FRAGS #define MYNEWT_VAL_BLE_L2CAP_JOIN_RX_FRAGS (1) #endif diff --git a/porting/nimble/include/syscfg/syscfg.h b/porting/nimble/include/syscfg/syscfg.h index 0b2ee4153..5a8995438 100644 --- a/porting/nimble/include/syscfg/syscfg.h +++ b/porting/nimble/include/syscfg/syscfg.h @@ -628,6 +628,10 @@ #define MYNEWT_VAL_BLE_L2CAP_COC_MPS (MYNEWT_VAL_MSYS_1_BLOCK_SIZE-8) #endif +#ifndef MYNEWT_VAL_BLE_L2CAP_ENHANCED_COC +#define MYNEWT_VAL_BLE_L2CAP_ENHANCED_COC (0) +#endif + #ifndef MYNEWT_VAL_BLE_L2CAP_JOIN_RX_FRAGS #define MYNEWT_VAL_BLE_L2CAP_JOIN_RX_FRAGS (1) #endif diff --git a/porting/npl/riot/include/syscfg/syscfg.h b/porting/npl/riot/include/syscfg/syscfg.h index 3d98b137a..e78ebe0a3 100644 --- a/porting/npl/riot/include/syscfg/syscfg.h +++ b/porting/npl/riot/include/syscfg/syscfg.h @@ -1296,6 +1296,10 @@ #define MYNEWT_VAL_BLE_L2CAP_COC_MPS (MYNEWT_VAL_MSYS_1_BLOCK_SIZE-8) #endif +#ifndef MYNEWT_VAL_BLE_L2CAP_ENHANCED_COC +#define MYNEWT_VAL_BLE_L2CAP_ENHANCED_COC (0) +#endif + #ifndef MYNEWT_VAL_BLE_L2CAP_JOIN_RX_FRAGS #define MYNEWT_VAL_BLE_L2CAP_JOIN_RX_FRAGS (1) #endif