| /* |
| * Copyright (c) 2020 Demant |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/types.h> |
| |
| #include <zephyr/bluetooth/hci.h> |
| #include <zephyr/sys/byteorder.h> |
| #include <zephyr/sys/slist.h> |
| #include <zephyr/sys/util.h> |
| |
| #include "hal/ccm.h" |
| |
| #include "util/util.h" |
| #include "util/mem.h" |
| #include "util/memq.h" |
| #include "util/dbuf.h" |
| |
| #include "pdu_df.h" |
| #include "lll/pdu_vendor.h" |
| #include "pdu.h" |
| |
| #include "ll.h" |
| #include "ll_settings.h" |
| |
| #include "lll.h" |
| #include "lll_clock.h" |
| #include "lll/lll_df_types.h" |
| #include "lll_conn.h" |
| #include "lll_conn_iso.h" |
| |
| #include "ull_tx_queue.h" |
| |
| #include "isoal.h" |
| #include "ull_iso_types.h" |
| #include "ull_conn_iso_types.h" |
| #include "ull_conn_iso_internal.h" |
| |
| #include "ull_conn_types.h" |
| #include "ull_llcp.h" |
| #include "ull_llcp_internal.h" |
| |
| #include "ll_feat.h" |
| |
| #include <soc.h> |
| #include "hal/debug.h" |
| |
| /* |
| * we need to filter on octet 0; the following mask has 7 octets |
| * with all bits set to 1, the last octet is 0x00 |
| */ |
| #define FEAT_FILT_OCTET0 0xFFFFFFFFFFFFFF00 |
| |
| #if defined(CONFIG_BT_CTLR_LE_PING) |
| /* |
| * LE Ping Procedure Helpers |
| */ |
| |
| void llcp_pdu_encode_ping_req(struct pdu_data *pdu) |
| { |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(ping_req); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PING_REQ; |
| } |
| |
| void llcp_pdu_encode_ping_rsp(struct pdu_data *pdu) |
| { |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(ping_rsp); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PING_RSP; |
| } |
| #endif /* CONFIG_BT_CTLR_LE_PING */ |
| /* |
| * Unknown response helper |
| */ |
| |
| void llcp_pdu_encode_unknown_rsp(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(unknown_rsp); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_UNKNOWN_RSP; |
| |
| pdu->llctrl.unknown_rsp.type = ctx->unknown_response.type; |
| } |
| |
| void llcp_pdu_decode_unknown_rsp(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| ctx->unknown_response.type = pdu->llctrl.unknown_rsp.type; |
| } |
| |
| void llcp_ntf_encode_unknown_rsp(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| struct pdu_data_llctrl_unknown_rsp *p; |
| |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(unknown_rsp); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_UNKNOWN_RSP; |
| p = &pdu->llctrl.unknown_rsp; |
| p->type = ctx->unknown_response.type; |
| } |
| |
| /* |
| * Feature Exchange Procedure Helper |
| */ |
| |
| static void feature_filter(uint8_t *featuresin, uint64_t *featuresout) |
| { |
| uint64_t feat; |
| |
| /* |
| * Note that in the split controller invalid bits are set |
| * to 1, in LLCP we set them to 0; the spec. does not |
| * define which value they should have |
| */ |
| feat = sys_get_le64(featuresin); |
| feat &= LL_FEAT_BIT_MASK; |
| feat &= LL_FEAT_BIT_MASK_VALID; |
| |
| *featuresout = feat; |
| } |
| |
| void llcp_pdu_encode_feature_req(struct ll_conn *conn, struct pdu_data *pdu) |
| { |
| struct pdu_data_llctrl_feature_req *p; |
| |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(feature_req); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_FEATURE_REQ; |
| |
| #if defined(CONFIG_BT_CTLR_PER_INIT_FEAT_XCHG) && defined(CONFIG_BT_PERIPHERAL) |
| if (conn->lll.role == BT_HCI_ROLE_PERIPHERAL) { |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PER_INIT_FEAT_XCHG; |
| } |
| #endif /* CONFIG_BT_CTLR_PER_INIT_FEAT_XCHG && CONFIG_BT_PERIPHERAL */ |
| |
| p = &pdu->llctrl.feature_req; |
| sys_put_le64(ll_feat_get(), p->features); |
| } |
| |
| void llcp_pdu_encode_feature_rsp(struct ll_conn *conn, struct pdu_data *pdu) |
| { |
| struct pdu_data_llctrl_feature_rsp *p; |
| uint64_t feature_rsp = ll_feat_get(); |
| |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(feature_rsp); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_FEATURE_RSP; |
| |
| p = &pdu->llctrl.feature_rsp; |
| |
| /* |
| * we only filter on octet 0, remaining 7 octets are the features |
| * we support, as defined in LL_FEAT |
| */ |
| feature_rsp &= (FEAT_FILT_OCTET0 | conn->llcp.fex.features_used); |
| |
| sys_put_le64(feature_rsp, p->features); |
| } |
| |
| void llcp_ntf_encode_feature_rsp(struct ll_conn *conn, struct pdu_data *pdu) |
| { |
| struct pdu_data_llctrl_feature_rsp *p; |
| |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(feature_rsp); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_FEATURE_RSP; |
| p = &pdu->llctrl.feature_rsp; |
| |
| sys_put_le64(conn->llcp.fex.features_peer, p->features); |
| } |
| |
| static uint64_t features_used(uint64_t featureset) |
| { |
| uint64_t x; |
| |
| /* swap bits for role specific features */ |
| x = ((featureset >> BT_LE_FEAT_BIT_CIS_CENTRAL) ^ |
| (featureset >> BT_LE_FEAT_BIT_CIS_PERIPHERAL)) & 0x01; |
| x = (x << BT_LE_FEAT_BIT_CIS_CENTRAL) | |
| (x << BT_LE_FEAT_BIT_CIS_PERIPHERAL); |
| x ^= featureset; |
| |
| return ll_feat_get() & x; |
| } |
| |
| void llcp_pdu_decode_feature_req(struct ll_conn *conn, struct pdu_data *pdu) |
| { |
| uint64_t featureset; |
| |
| feature_filter(pdu->llctrl.feature_req.features, &featureset); |
| conn->llcp.fex.features_used = features_used(featureset); |
| |
| featureset &= (FEAT_FILT_OCTET0 | conn->llcp.fex.features_used); |
| conn->llcp.fex.features_peer = featureset; |
| |
| conn->llcp.fex.valid = 1; |
| } |
| |
| void llcp_pdu_decode_feature_rsp(struct ll_conn *conn, struct pdu_data *pdu) |
| { |
| uint64_t featureset; |
| |
| feature_filter(pdu->llctrl.feature_rsp.features, &featureset); |
| conn->llcp.fex.features_used = features_used(featureset); |
| conn->llcp.fex.features_peer = featureset; |
| conn->llcp.fex.valid = 1; |
| } |
| |
| #if defined(CONFIG_BT_CTLR_MIN_USED_CHAN) |
| /* |
| * Minimum used channels Procedure Helpers |
| */ |
| #if defined(CONFIG_BT_PERIPHERAL) |
| void llcp_pdu_encode_min_used_chans_ind(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| struct pdu_data_llctrl_min_used_chans_ind *p; |
| |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(min_used_chans_ind); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_MIN_USED_CHAN_IND; |
| p = &pdu->llctrl.min_used_chans_ind; |
| p->phys = ctx->data.muc.phys; |
| p->min_used_chans = ctx->data.muc.min_used_chans; |
| } |
| #endif /* CONFIG_BT_PERIPHERAL */ |
| |
| #if defined(CONFIG_BT_CENTRAL) |
| void llcp_pdu_decode_min_used_chans_ind(struct ll_conn *conn, struct pdu_data *pdu) |
| { |
| conn->llcp.muc.phys = pdu->llctrl.min_used_chans_ind.phys; |
| conn->llcp.muc.min_used_chans = pdu->llctrl.min_used_chans_ind.min_used_chans; |
| } |
| #endif /* CONFIG_BT_CENTRAL */ |
| #endif /* CONFIG_BT_CTLR_MIN_USED_CHAN */ |
| |
| /* |
| * Termination Procedure Helper |
| */ |
| void llcp_pdu_encode_terminate_ind(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| struct pdu_data_llctrl_terminate_ind *p; |
| |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(terminate_ind); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_TERMINATE_IND; |
| p = &pdu->llctrl.terminate_ind; |
| p->error_code = ctx->data.term.error_code; |
| } |
| |
| void llcp_pdu_decode_terminate_ind(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| ctx->data.term.error_code = pdu->llctrl.terminate_ind.error_code; |
| } |
| |
| /* |
| * Version Exchange Procedure Helper |
| */ |
| void llcp_pdu_encode_version_ind(struct pdu_data *pdu) |
| { |
| uint16_t cid; |
| uint16_t svn; |
| struct pdu_data_llctrl_version_ind *p; |
| |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(version_ind); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_VERSION_IND; |
| |
| p = &pdu->llctrl.version_ind; |
| p->version_number = LL_VERSION_NUMBER; |
| cid = sys_cpu_to_le16(ll_settings_company_id()); |
| svn = sys_cpu_to_le16(ll_settings_subversion_number()); |
| p->company_id = cid; |
| p->sub_version_number = svn; |
| } |
| |
| void llcp_ntf_encode_version_ind(struct ll_conn *conn, struct pdu_data *pdu) |
| { |
| struct pdu_data_llctrl_version_ind *p; |
| |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(version_ind); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_VERSION_IND; |
| |
| p = &pdu->llctrl.version_ind; |
| p->version_number = conn->llcp.vex.cached.version_number; |
| p->company_id = sys_cpu_to_le16(conn->llcp.vex.cached.company_id); |
| p->sub_version_number = sys_cpu_to_le16(conn->llcp.vex.cached.sub_version_number); |
| } |
| |
| void llcp_pdu_decode_version_ind(struct ll_conn *conn, struct pdu_data *pdu) |
| { |
| conn->llcp.vex.valid = 1; |
| conn->llcp.vex.cached.version_number = pdu->llctrl.version_ind.version_number; |
| conn->llcp.vex.cached.company_id = sys_le16_to_cpu(pdu->llctrl.version_ind.company_id); |
| conn->llcp.vex.cached.sub_version_number = |
| sys_le16_to_cpu(pdu->llctrl.version_ind.sub_version_number); |
| } |
| |
| #if defined(CONFIG_BT_CTLR_LE_ENC) |
| /* |
| * Encryption Start Procedure Helper |
| */ |
| |
| static int csrand_get(void *buf, size_t len) |
| { |
| if (k_is_in_isr()) { |
| return lll_csrand_isr_get(buf, len); |
| } else { |
| return lll_csrand_get(buf, len); |
| } |
| } |
| |
| #if defined(CONFIG_BT_CENTRAL) || defined(CONFIG_BT_PERIPHERAL) |
| static void encode_enc_req(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| struct pdu_data_llctrl_enc_req *p; |
| |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(enc_req); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_ENC_REQ; |
| |
| p = &pdu->llctrl.enc_req; |
| memcpy(p->rand, ctx->data.enc.rand, sizeof(p->rand)); |
| p->ediv[0] = ctx->data.enc.ediv[0]; |
| p->ediv[1] = ctx->data.enc.ediv[1]; |
| } |
| #endif /* CONFIG_BT_CENTRAL || CONFIG_BT_PERIPHERAL */ |
| |
| #if defined(CONFIG_BT_CENTRAL) |
| void llcp_pdu_encode_enc_req(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| struct pdu_data_llctrl_enc_req *p; |
| |
| encode_enc_req(ctx, pdu); |
| |
| /* Optimal getting random data, p->ivm is packed right after p->skdm */ |
| p = &pdu->llctrl.enc_req; |
| BUILD_ASSERT(offsetof(struct pdu_data_llctrl_enc_req, ivm) == |
| offsetof(struct pdu_data_llctrl_enc_req, skdm) + sizeof(p->skdm), |
| "Member IVM must be after member SKDM"); |
| csrand_get(p->skdm, sizeof(p->skdm) + sizeof(p->ivm)); |
| |
| } |
| #endif /* CONFIG_BT_CENTRAL */ |
| |
| #if defined(CONFIG_BT_PERIPHERAL) |
| void llcp_ntf_encode_enc_req(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| encode_enc_req(ctx, pdu); |
| } |
| |
| void llcp_pdu_encode_enc_rsp(struct pdu_data *pdu) |
| { |
| struct pdu_data_llctrl_enc_rsp *p; |
| |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(enc_rsp); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_ENC_RSP; |
| |
| p = &pdu->llctrl.enc_rsp; |
| /* Optimal getting random data, p->ivs is packed right after p->skds */ |
| BUILD_ASSERT(offsetof(struct pdu_data_llctrl_enc_rsp, ivs) == |
| offsetof(struct pdu_data_llctrl_enc_rsp, skds) + sizeof(p->skds), |
| "Member IVS must be after member SKDS"); |
| csrand_get(p->skds, sizeof(p->skds) + sizeof(p->ivs)); |
| } |
| |
| void llcp_pdu_encode_start_enc_req(struct pdu_data *pdu) |
| { |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(start_enc_req); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_START_ENC_REQ; |
| } |
| #endif /* CONFIG_BT_PERIPHERAL */ |
| |
| void llcp_pdu_encode_start_enc_rsp(struct pdu_data *pdu) |
| { |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(start_enc_rsp); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_START_ENC_RSP; |
| } |
| |
| #if defined(CONFIG_BT_CENTRAL) |
| void llcp_pdu_encode_pause_enc_req(struct pdu_data *pdu) |
| { |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(pause_enc_req); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_REQ; |
| } |
| #endif /* CONFIG_BT_CENTRAL */ |
| |
| void llcp_pdu_encode_pause_enc_rsp(struct pdu_data *pdu) |
| { |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(pause_enc_rsp); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_RSP; |
| } |
| #endif /* CONFIG_BT_CTLR_LE_ENC */ |
| |
| void llcp_pdu_encode_reject_ind(struct pdu_data *pdu, uint8_t error_code) |
| { |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(reject_ind); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_REJECT_IND; |
| pdu->llctrl.reject_ind.error_code = error_code; |
| } |
| |
| void llcp_pdu_encode_reject_ext_ind(struct pdu_data *pdu, uint8_t reject_opcode, uint8_t error_code) |
| { |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(reject_ext_ind); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND; |
| pdu->llctrl.reject_ext_ind.reject_opcode = reject_opcode; |
| pdu->llctrl.reject_ext_ind.error_code = error_code; |
| } |
| |
| void llcp_pdu_decode_reject_ext_ind(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| ctx->reject_ext_ind.reject_opcode = pdu->llctrl.reject_ext_ind.reject_opcode; |
| ctx->reject_ext_ind.error_code = pdu->llctrl.reject_ext_ind.error_code; |
| } |
| |
| void llcp_ntf_encode_reject_ext_ind(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| struct pdu_data_llctrl_reject_ext_ind *p; |
| |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(reject_ext_ind); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND; |
| |
| p = (void *)&pdu->llctrl.reject_ext_ind; |
| p->error_code = ctx->reject_ext_ind.error_code; |
| p->reject_opcode = ctx->reject_ext_ind.reject_opcode; |
| } |
| |
| #ifdef CONFIG_BT_CTLR_PHY |
| /* |
| * PHY Update Procedure Helper |
| */ |
| |
| void llcp_pdu_encode_phy_req(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(phy_req); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PHY_REQ; |
| pdu->llctrl.phy_req.rx_phys = ctx->data.pu.rx; |
| pdu->llctrl.phy_req.tx_phys = ctx->data.pu.tx; |
| } |
| |
| void llcp_pdu_decode_phy_req(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| ctx->data.pu.rx = pdu->llctrl.phy_req.tx_phys; |
| ctx->data.pu.tx = pdu->llctrl.phy_req.rx_phys; |
| } |
| |
| #if defined(CONFIG_BT_PERIPHERAL) |
| void llcp_pdu_encode_phy_rsp(struct ll_conn *conn, struct pdu_data *pdu) |
| { |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(phy_rsp); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PHY_RSP; |
| pdu->llctrl.phy_rsp.rx_phys = conn->phy_pref_rx; |
| pdu->llctrl.phy_rsp.tx_phys = conn->phy_pref_tx; |
| } |
| void llcp_pdu_decode_phy_update_ind(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| ctx->data.pu.instant = sys_le16_to_cpu(pdu->llctrl.phy_upd_ind.instant); |
| ctx->data.pu.c_to_p_phy = pdu->llctrl.phy_upd_ind.c_to_p_phy; |
| ctx->data.pu.p_to_c_phy = pdu->llctrl.phy_upd_ind.p_to_c_phy; |
| } |
| #endif /* CONFIG_BT_PERIPHERAL */ |
| |
| #if defined(CONFIG_BT_CENTRAL) |
| void llcp_pdu_encode_phy_update_ind(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(phy_upd_ind); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PHY_UPD_IND; |
| pdu->llctrl.phy_upd_ind.instant = sys_cpu_to_le16(ctx->data.pu.instant); |
| pdu->llctrl.phy_upd_ind.c_to_p_phy = ctx->data.pu.c_to_p_phy; |
| pdu->llctrl.phy_upd_ind.p_to_c_phy = ctx->data.pu.p_to_c_phy; |
| } |
| void llcp_pdu_decode_phy_rsp(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| ctx->data.pu.rx = pdu->llctrl.phy_rsp.tx_phys; |
| ctx->data.pu.tx = pdu->llctrl.phy_rsp.rx_phys; |
| } |
| #endif /* CONFIG_BT_CENTRAL */ |
| #endif /* CONFIG_BT_CTLR_PHY */ |
| |
| #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) |
| /* |
| * Connection Update Procedure Helper |
| */ |
| static void encode_conn_param_req_rsp_common(struct proc_ctx *ctx, struct pdu_data *pdu, |
| struct pdu_data_llctrl_conn_param_req_rsp_common *p, |
| uint8_t opcode) |
| { |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| /* The '+ 1U' is to count in opcode octet, the first member of struct pdu_data_llctrl */ |
| pdu->len = sizeof(struct pdu_data_llctrl_conn_param_req_rsp_common) + 1U; |
| pdu->llctrl.opcode = opcode; |
| |
| p->interval_min = sys_cpu_to_le16(ctx->data.cu.interval_min); |
| p->interval_max = sys_cpu_to_le16(ctx->data.cu.interval_max); |
| p->latency = sys_cpu_to_le16(ctx->data.cu.latency); |
| p->timeout = sys_cpu_to_le16(ctx->data.cu.timeout); |
| p->preferred_periodicity = ctx->data.cu.preferred_periodicity; |
| p->reference_conn_event_count = sys_cpu_to_le16(ctx->data.cu.reference_conn_event_count); |
| p->offset0 = sys_cpu_to_le16(ctx->data.cu.offsets[0]); |
| p->offset1 = sys_cpu_to_le16(ctx->data.cu.offsets[1]); |
| p->offset2 = sys_cpu_to_le16(ctx->data.cu.offsets[2]); |
| p->offset3 = sys_cpu_to_le16(ctx->data.cu.offsets[3]); |
| p->offset4 = sys_cpu_to_le16(ctx->data.cu.offsets[4]); |
| p->offset5 = sys_cpu_to_le16(ctx->data.cu.offsets[5]); |
| } |
| |
| |
| void llcp_pdu_encode_conn_param_req(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| encode_conn_param_req_rsp_common(ctx, pdu, |
| (struct pdu_data_llctrl_conn_param_req_rsp_common *)&pdu->llctrl.conn_param_req, |
| PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ); |
| } |
| |
| void llcp_pdu_encode_conn_param_rsp(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| encode_conn_param_req_rsp_common(ctx, pdu, |
| (struct pdu_data_llctrl_conn_param_req_rsp_common *)&pdu->llctrl.conn_param_rsp, |
| PDU_DATA_LLCTRL_TYPE_CONN_PARAM_RSP); |
| } |
| |
| static void decode_conn_param_req_rsp_common(struct proc_ctx *ctx, |
| struct pdu_data_llctrl_conn_param_req_rsp_common *p) |
| { |
| ctx->data.cu.interval_min = sys_le16_to_cpu(p->interval_min); |
| ctx->data.cu.interval_max = sys_le16_to_cpu(p->interval_max); |
| ctx->data.cu.latency = sys_le16_to_cpu(p->latency); |
| ctx->data.cu.timeout = sys_le16_to_cpu(p->timeout); |
| ctx->data.cu.preferred_periodicity = p->preferred_periodicity; |
| ctx->data.cu.reference_conn_event_count = sys_le16_to_cpu(p->reference_conn_event_count); |
| ctx->data.cu.offsets[0] = sys_le16_to_cpu(p->offset0); |
| ctx->data.cu.offsets[1] = sys_le16_to_cpu(p->offset1); |
| ctx->data.cu.offsets[2] = sys_le16_to_cpu(p->offset2); |
| ctx->data.cu.offsets[3] = sys_le16_to_cpu(p->offset3); |
| ctx->data.cu.offsets[4] = sys_le16_to_cpu(p->offset4); |
| ctx->data.cu.offsets[5] = sys_le16_to_cpu(p->offset5); |
| } |
| |
| void llcp_pdu_decode_conn_param_req(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| decode_conn_param_req_rsp_common(ctx, |
| (struct pdu_data_llctrl_conn_param_req_rsp_common *)&pdu->llctrl.conn_param_req); |
| } |
| |
| void llcp_pdu_decode_conn_param_rsp(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| decode_conn_param_req_rsp_common(ctx, |
| (struct pdu_data_llctrl_conn_param_req_rsp_common *)&pdu->llctrl.conn_param_rsp); |
| } |
| #endif /* defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) */ |
| |
| void llcp_pdu_encode_conn_update_ind(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| struct pdu_data_llctrl_conn_update_ind *p; |
| |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(conn_update_ind); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND; |
| |
| p = (void *)&pdu->llctrl.conn_update_ind; |
| p->win_size = ctx->data.cu.win_size; |
| p->win_offset = sys_cpu_to_le16(ctx->data.cu.win_offset_us / CONN_INT_UNIT_US); |
| p->latency = sys_cpu_to_le16(ctx->data.cu.latency); |
| p->interval = sys_cpu_to_le16(ctx->data.cu.interval_max); |
| p->timeout = sys_cpu_to_le16(ctx->data.cu.timeout); |
| p->instant = sys_cpu_to_le16(ctx->data.cu.instant); |
| } |
| |
| void llcp_pdu_decode_conn_update_ind(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| struct pdu_data_llctrl_conn_update_ind *p; |
| |
| p = (void *)&pdu->llctrl.conn_update_ind; |
| ctx->data.cu.win_size = p->win_size; |
| ctx->data.cu.win_offset_us = sys_le16_to_cpu(p->win_offset) * CONN_INT_UNIT_US; |
| ctx->data.cu.latency = sys_le16_to_cpu(p->latency); |
| ctx->data.cu.interval_max = sys_le16_to_cpu(p->interval); |
| ctx->data.cu.timeout = sys_le16_to_cpu(p->timeout); |
| ctx->data.cu.instant = sys_le16_to_cpu(p->instant); |
| } |
| |
| /* |
| * Channel Map Update Procedure Helpers |
| */ |
| void llcp_pdu_encode_chan_map_update_ind(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| struct pdu_data_llctrl_chan_map_ind *p; |
| |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(chan_map_ind); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CHAN_MAP_IND; |
| p = &pdu->llctrl.chan_map_ind; |
| p->instant = sys_cpu_to_le16(ctx->data.chmu.instant); |
| memcpy(p->chm, ctx->data.chmu.chm, sizeof(p->chm)); |
| } |
| |
| void llcp_pdu_decode_chan_map_update_ind(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| ctx->data.chmu.instant = sys_le16_to_cpu(pdu->llctrl.chan_map_ind.instant); |
| memcpy(ctx->data.chmu.chm, pdu->llctrl.chan_map_ind.chm, sizeof(ctx->data.chmu.chm)); |
| } |
| |
| #if defined(CONFIG_BT_CTLR_DATA_LENGTH) |
| /* |
| * Data Length Update Procedure Helpers |
| */ |
| static void encode_length_req_rsp_common(struct pdu_data *pdu, |
| struct pdu_data_llctrl_length_req_rsp_common *p, |
| const uint8_t opcode, |
| const struct data_pdu_length *dle) |
| { |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| /* The '+ 1U' is to count in opcode octet, the first member of struct pdu_data_llctrl */ |
| pdu->len = sizeof(struct pdu_data_llctrl_length_req_rsp_common) + 1U; |
| pdu->llctrl.opcode = opcode; |
| p->max_rx_octets = sys_cpu_to_le16(dle->max_rx_octets); |
| p->max_tx_octets = sys_cpu_to_le16(dle->max_tx_octets); |
| p->max_rx_time = sys_cpu_to_le16(dle->max_rx_time); |
| p->max_tx_time = sys_cpu_to_le16(dle->max_tx_time); |
| } |
| |
| void llcp_pdu_encode_length_req(struct ll_conn *conn, struct pdu_data *pdu) |
| { |
| encode_length_req_rsp_common(pdu, |
| (struct pdu_data_llctrl_length_req_rsp_common *)&pdu->llctrl.length_req, |
| PDU_DATA_LLCTRL_TYPE_LENGTH_REQ, |
| &conn->lll.dle.local); |
| } |
| |
| void llcp_pdu_encode_length_rsp(struct ll_conn *conn, struct pdu_data *pdu) |
| { |
| encode_length_req_rsp_common(pdu, |
| (struct pdu_data_llctrl_length_req_rsp_common *)&pdu->llctrl.length_rsp, |
| PDU_DATA_LLCTRL_TYPE_LENGTH_RSP, |
| &conn->lll.dle.local); |
| } |
| |
| void llcp_ntf_encode_length_change(struct ll_conn *conn, struct pdu_data *pdu) |
| { |
| encode_length_req_rsp_common(pdu, |
| (struct pdu_data_llctrl_length_req_rsp_common *)&pdu->llctrl.length_rsp, |
| PDU_DATA_LLCTRL_TYPE_LENGTH_RSP, |
| &conn->lll.dle.eff); |
| } |
| |
| static bool dle_remote_valid(const struct data_pdu_length *remote) |
| { |
| if (!IN_RANGE(remote->max_rx_octets, PDU_DC_PAYLOAD_SIZE_MIN, |
| PDU_DC_PAYLOAD_SIZE_MAX)) { |
| return false; |
| } |
| |
| if (!IN_RANGE(remote->max_tx_octets, PDU_DC_PAYLOAD_SIZE_MIN, |
| PDU_DC_PAYLOAD_SIZE_MAX)) { |
| return false; |
| } |
| |
| #if defined(CONFIG_BT_CTLR_PHY) |
| if (!IN_RANGE(remote->max_rx_time, PDU_DC_PAYLOAD_TIME_MIN, |
| PDU_DC_PAYLOAD_TIME_MAX_CODED)) { |
| return false; |
| } |
| |
| if (!IN_RANGE(remote->max_tx_time, PDU_DC_PAYLOAD_TIME_MIN, |
| PDU_DC_PAYLOAD_TIME_MAX_CODED)) { |
| return false; |
| } |
| #else |
| if (!IN_RANGE(remote->max_rx_time, PDU_DC_PAYLOAD_TIME_MIN, |
| PDU_DC_PAYLOAD_TIME_MAX)) { |
| return false; |
| } |
| |
| if (!IN_RANGE(remote->max_tx_time, PDU_DC_PAYLOAD_TIME_MIN, |
| PDU_DC_PAYLOAD_TIME_MAX)) { |
| return false; |
| } |
| #endif /* CONFIG_BT_CTLR_PHY */ |
| |
| return true; |
| } |
| static void decode_length_req_rsp_common(struct ll_conn *conn, |
| struct pdu_data_llctrl_length_req_rsp_common *p) |
| { |
| struct data_pdu_length remote; |
| |
| remote.max_rx_octets = sys_le16_to_cpu(p->max_rx_octets); |
| remote.max_tx_octets = sys_le16_to_cpu(p->max_tx_octets); |
| remote.max_rx_time = sys_le16_to_cpu(p->max_rx_time); |
| remote.max_tx_time = sys_le16_to_cpu(p->max_tx_time); |
| |
| if (!dle_remote_valid(&remote)) { |
| return; |
| } |
| |
| conn->lll.dle.remote = remote; |
| } |
| |
| void llcp_pdu_decode_length_req(struct ll_conn *conn, struct pdu_data *pdu) |
| { |
| decode_length_req_rsp_common(conn, |
| (struct pdu_data_llctrl_length_req_rsp_common *)&pdu->llctrl.length_req); |
| } |
| |
| void llcp_pdu_decode_length_rsp(struct ll_conn *conn, struct pdu_data *pdu) |
| { |
| decode_length_req_rsp_common(conn, |
| (struct pdu_data_llctrl_length_req_rsp_common *)&pdu->llctrl.length_rsp); |
| } |
| #endif /* CONFIG_BT_CTLR_DATA_LENGTH */ |
| |
| #if defined(CONFIG_BT_CTLR_DF_CONN_CTE_REQ) |
| /* |
| * Constant Tone Request Procedure Helper |
| */ |
| void llcp_pdu_encode_cte_req(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| struct pdu_data_llctrl_cte_req *p; |
| |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(cte_req); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CTE_REQ; |
| |
| p = &pdu->llctrl.cte_req; |
| p->min_cte_len_req = ctx->data.cte_req.min_len; |
| p->rfu = 0; /* Reserved for future use */ |
| p->cte_type_req = ctx->data.cte_req.type; |
| } |
| |
| void llcp_pdu_decode_cte_rsp(struct proc_ctx *ctx, const struct pdu_data *pdu) |
| { |
| if (pdu->cp == 0U || pdu->octet3.cte_info.time == 0U) { |
| ctx->data.cte_remote_rsp.has_cte = false; |
| } else { |
| ctx->data.cte_remote_rsp.has_cte = true; |
| } |
| } |
| |
| void llcp_ntf_encode_cte_req(struct pdu_data *pdu) |
| { |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(cte_rsp); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CTE_RSP; |
| |
| /* Received LL_CTE_RSP PDU didn't have CTE */ |
| pdu->cp = 0U; |
| } |
| #endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */ |
| |
| #if defined(CONFIG_BT_CTLR_DF_CONN_CTE_RSP) |
| void llcp_pdu_decode_cte_req(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| ctx->data.cte_remote_req.min_cte_len = pdu->llctrl.cte_req.min_cte_len_req; |
| ctx->data.cte_remote_req.cte_type = pdu->llctrl.cte_req.cte_type_req; |
| } |
| |
| void llcp_pdu_encode_cte_rsp(const struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(cte_rsp); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CTE_RSP; |
| |
| /* Set content of a PDU header first byte, that is not changed by LLL */ |
| pdu->cp = 1U; |
| pdu->rfu = 0U; |
| |
| pdu->octet3.cte_info.time = ctx->data.cte_remote_req.min_cte_len; |
| pdu->octet3.cte_info.type = ctx->data.cte_remote_req.cte_type; |
| } |
| #endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RSP */ |
| |
| #if defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) |
| void llcp_pdu_decode_cis_req(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| ctx->data.cis_create.cig_id = pdu->llctrl.cis_req.cig_id; |
| ctx->data.cis_create.cis_id = pdu->llctrl.cis_req.cis_id; |
| ctx->data.cis_create.cis_offset_min = sys_get_le24(pdu->llctrl.cis_req.cis_offset_min); |
| ctx->data.cis_create.cis_offset_max = sys_get_le24(pdu->llctrl.cis_req.cis_offset_max); |
| ctx->data.cis_create.conn_event_count = |
| sys_le16_to_cpu(pdu->llctrl.cis_req.conn_event_count); |
| ctx->data.cis_create.iso_interval = sys_le16_to_cpu(pdu->llctrl.cis_req.iso_interval); |
| /* The remainder of the req is decoded by ull_peripheral_iso_acquire, so |
| * no need to do it here too |
| ctx->data.cis_create.c_phy = pdu->llctrl.cis_req.c_phy; |
| ctx->data.cis_create.p_phy = pdu->llctrl.cis_req.p_phy; |
| ctx->data.cis_create.c_max_sdu = pdu->llctrl.cis_req.c_max_sdu; |
| ctx->data.cis_create.p_max_sdu = pdu->llctrl.cis_req.p_max_sdu; |
| ctx->data.cis_create.framed = pdu->llctrl.cis_req.framed; |
| ctx->data.cis_create.c_sdu_interval = sys_get_le24(pdu->llctrl.cis_req.c_sdu_interval); |
| ctx->data.cis_create.p_sdu_interval = sys_get_le24(pdu->llctrl.cis_req.p_sdu_interval); |
| ctx->data.cis_create.nse = pdu->llctrl.cis_req.nse; |
| ctx->data.cis_create.c_max_pdu = sys_le16_to_cpu(pdu->llctrl.cis_req.c_max_pdu); |
| ctx->data.cis_create.p_max_pdu = sys_le16_to_cpu(pdu->llctrl.cis_req.p_max_pdu); |
| ctx->data.cis_create.sub_interval = sys_get_le24(pdu->llctrl.cis_req.sub_interval); |
| ctx->data.cis_create.p_bn = pdu->llctrl.cis_req.p_bn; |
| ctx->data.cis_create.c_bn = pdu->llctrl.cis_req.c_bn; |
| ctx->data.cis_create.c_ft = pdu->llctrl.cis_req.c_ft; |
| ctx->data.cis_create.p_ft = pdu->llctrl.cis_req.p_ft; |
| */ |
| } |
| |
| void llcp_pdu_decode_cis_ind(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| ctx->data.cis_create.conn_event_count = |
| sys_le16_to_cpu(pdu->llctrl.cis_ind.conn_event_count); |
| /* The remainder of the cis ind is decoded by ull_peripheral_iso_setup, so |
| * no need to do it here too |
| */ |
| } |
| |
| void llcp_pdu_encode_cis_rsp(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| struct pdu_data_llctrl_cis_rsp *p; |
| |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(cis_rsp); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CIS_RSP; |
| |
| p = &pdu->llctrl.cis_rsp; |
| sys_put_le24(ctx->data.cis_create.cis_offset_max, p->cis_offset_max); |
| sys_put_le24(ctx->data.cis_create.cis_offset_min, p->cis_offset_min); |
| p->conn_event_count = sys_cpu_to_le16(ctx->data.cis_create.conn_event_count); |
| } |
| #endif /* defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) */ |
| |
| #if defined(CONFIG_BT_CTLR_CENTRAL_ISO) |
| void llcp_pdu_encode_cis_req(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| struct pdu_data_llctrl_cis_req *p; |
| |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = offsetof(struct pdu_data_llctrl, cis_req) + |
| sizeof(struct pdu_data_llctrl_cis_req); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CIS_REQ; |
| |
| p = &pdu->llctrl.cis_req; |
| |
| p->cig_id = ctx->data.cis_create.cig_id; |
| p->cis_id = ctx->data.cis_create.cis_id; |
| p->c_phy = ctx->data.cis_create.c_phy; |
| p->p_phy = ctx->data.cis_create.p_phy; |
| p->nse = ctx->data.cis_create.nse; |
| p->c_bn = ctx->data.cis_create.c_bn; |
| p->p_bn = ctx->data.cis_create.p_bn; |
| p->c_ft = ctx->data.cis_create.c_ft; |
| p->p_ft = ctx->data.cis_create.p_ft; |
| |
| sys_put_le24(ctx->data.cis_create.c_sdu_interval, p->c_sdu_interval); |
| sys_put_le24(ctx->data.cis_create.p_sdu_interval, p->p_sdu_interval); |
| sys_put_le24(ctx->data.cis_create.cis_offset_max, p->cis_offset_max); |
| sys_put_le24(ctx->data.cis_create.cis_offset_min, p->cis_offset_min); |
| sys_put_le24(ctx->data.cis_create.sub_interval, p->sub_interval); |
| |
| p->c_max_pdu = sys_cpu_to_le16(ctx->data.cis_create.c_max_pdu); |
| p->p_max_pdu = sys_cpu_to_le16(ctx->data.cis_create.p_max_pdu); |
| p->iso_interval = sys_cpu_to_le16(ctx->data.cis_create.iso_interval); |
| p->conn_event_count = sys_cpu_to_le16(ctx->data.cis_create.conn_event_count); |
| |
| p->c_max_sdu_packed[0] = ctx->data.cis_create.c_max_sdu & 0xFF; |
| p->c_max_sdu_packed[1] = ((ctx->data.cis_create.c_max_sdu >> 8) & 0x0F) | |
| (ctx->data.cis_create.framed << 7); |
| p->p_max_sdu[0] = ctx->data.cis_create.p_max_sdu & 0xFF; |
| p->p_max_sdu[1] = (ctx->data.cis_create.p_max_sdu >> 8) & 0x0F; |
| } |
| |
| void llcp_pdu_decode_cis_rsp(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| /* Limit response to valid range */ |
| uint32_t cis_offset_min = sys_get_le24(pdu->llctrl.cis_rsp.cis_offset_min); |
| uint32_t cis_offset_max = sys_get_le24(pdu->llctrl.cis_rsp.cis_offset_max); |
| |
| /* TODO: Fail procedure if offsets are invalid? */ |
| if (cis_offset_min <= cis_offset_max && |
| cis_offset_min >= ctx->data.cis_create.cis_offset_min && |
| cis_offset_min <= ctx->data.cis_create.cis_offset_max && |
| cis_offset_max <= ctx->data.cis_create.cis_offset_max && |
| cis_offset_max >= ctx->data.cis_create.cis_offset_min) { |
| /* Offsets are valid */ |
| ctx->data.cis_create.cis_offset_min = cis_offset_min; |
| ctx->data.cis_create.cis_offset_max = cis_offset_max; |
| } |
| |
| ctx->data.cis_create.conn_event_count = |
| sys_le16_to_cpu(pdu->llctrl.cis_rsp.conn_event_count); |
| } |
| |
| void llcp_pdu_encode_cis_ind(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| struct pdu_data_llctrl_cis_ind *p; |
| |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = offsetof(struct pdu_data_llctrl, cis_ind) + |
| sizeof(struct pdu_data_llctrl_cis_ind); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CIS_IND; |
| |
| p = &pdu->llctrl.cis_ind; |
| |
| p->aa[0] = ctx->data.cis_create.aa[0]; |
| p->aa[1] = ctx->data.cis_create.aa[1]; |
| p->aa[2] = ctx->data.cis_create.aa[2]; |
| p->aa[3] = ctx->data.cis_create.aa[3]; |
| |
| sys_put_le24(ctx->data.cis_create.cis_offset_min, p->cis_offset); |
| sys_put_le24(ctx->data.cis_create.cig_sync_delay, p->cig_sync_delay); |
| sys_put_le24(ctx->data.cis_create.cis_sync_delay, p->cis_sync_delay); |
| |
| p->conn_event_count = sys_cpu_to_le16(ctx->data.cis_create.conn_event_count); |
| } |
| #endif /* CONFIG_BT_CTLR_CENTRAL_ISO */ |
| |
| #if defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) |
| void llcp_pdu_encode_cis_terminate_ind(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| struct pdu_data_llctrl_cis_terminate_ind *p; |
| |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(cis_terminate_ind); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CIS_TERMINATE_IND; |
| |
| p = &pdu->llctrl.cis_terminate_ind; |
| p->cig_id = ctx->data.cis_term.cig_id; |
| p->cis_id = ctx->data.cis_term.cis_id; |
| p->error_code = ctx->data.cis_term.error_code; |
| } |
| |
| void llcp_pdu_decode_cis_terminate_ind(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| ctx->data.cis_term.cig_id = pdu->llctrl.cis_terminate_ind.cig_id; |
| ctx->data.cis_term.cis_id = pdu->llctrl.cis_terminate_ind.cis_id; |
| ctx->data.cis_term.error_code = pdu->llctrl.cis_terminate_ind.error_code; |
| } |
| #endif /* defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) */ |
| |
| #if defined(CONFIG_BT_CTLR_SCA_UPDATE) |
| /* |
| * SCA Update Procedure Helpers |
| */ |
| void llcp_pdu_encode_clock_accuracy_req(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| struct pdu_data_llctrl_clock_accuracy_req *p = &pdu->llctrl.clock_accuracy_req; |
| |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(clock_accuracy_req); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_REQ; |
| /* Currently we do not support variable SCA, so we always 'report' current SCA */ |
| p->sca = lll_clock_sca_local_get(); |
| } |
| |
| void llcp_pdu_encode_clock_accuracy_rsp(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| struct pdu_data_llctrl_clock_accuracy_rsp *p = &pdu->llctrl.clock_accuracy_rsp; |
| |
| pdu->ll_id = PDU_DATA_LLID_CTRL; |
| pdu->len = PDU_DATA_LLCTRL_LEN(clock_accuracy_rsp); |
| pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_RSP; |
| /* Currently we do not support variable SCA, so we always 'report' current SCA */ |
| p->sca = lll_clock_sca_local_get(); |
| } |
| |
| void llcp_pdu_decode_clock_accuracy_req(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| struct pdu_data_llctrl_clock_accuracy_req *p = &pdu->llctrl.clock_accuracy_req; |
| |
| ctx->data.sca_update.sca = p->sca; |
| } |
| |
| void llcp_pdu_decode_clock_accuracy_rsp(struct proc_ctx *ctx, struct pdu_data *pdu) |
| { |
| struct pdu_data_llctrl_clock_accuracy_rsp *p = &pdu->llctrl.clock_accuracy_rsp; |
| |
| ctx->data.sca_update.sca = p->sca; |
| } |
| #endif /* CONFIG_BT_CTLR_SCA_UPDATE */ |