static uint8_t att_read_group_rsp(struct bt_att *att, struct bt_uuid *uuid, uint16_t start_handle, uint16_t end_handle) { struct bt_conn *conn = att->chan.conn; struct read_group_data data; memset(&data, 0, sizeof(data)); data.buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_GROUP_RSP, sizeof(*data.rsp)); if (!data.buf) { return BT_ATT_ERR_UNLIKELY; } data.att = att; data.uuid = uuid; data.rsp = net_buf_add(data.buf, sizeof(*data.rsp)); data.rsp->len = 0; data.group = NULL; bt_gatt_foreach_attr(start_handle, end_handle, read_group_cb, &data); if (!data.rsp->len) { net_buf_unref(data.buf); /* Respond here since handle is set */ send_err_rsp(conn, BT_ATT_OP_READ_GROUP_REQ, start_handle, BT_ATT_ERR_ATTRIBUTE_NOT_FOUND); return 0; } bt_l2cap_send(conn, BT_L2CAP_CID_ATT, data.buf); return 0; }
static int l2cap_chan_le_send(struct bt_l2cap_le_chan *ch, struct net_buf *buf, uint16_t sdu_hdr_len) { int len; /* Wait for credits */ if (k_sem_take(&ch->tx.credits, K_NO_WAIT)) { BT_DBG("No credits to transmit packet"); return -EAGAIN; } buf = l2cap_chan_create_seg(ch, buf, sdu_hdr_len); if (!buf) { return -ENOMEM; } /* Channel may have been disconnected while waiting for credits */ if (!ch->chan.conn) { net_buf_unref(buf); return -ECONNRESET; } BT_DBG("ch %p cid 0x%04x len %u credits %u", ch, ch->tx.cid, buf->len, k_sem_count_get(&ch->tx.credits)); len = buf->len; bt_l2cap_send(ch->chan.conn, ch->tx.cid, buf); return len; }
static void l2cap_chan_update_credits(struct bt_l2cap_le_chan *chan) { struct net_buf *buf; struct bt_l2cap_le_credits *ev; uint16_t credits; /* Only give more credits if it went bellow the defined threshold */ if (k_sem_count_get(&chan->rx.credits) > L2CAP_LE_CREDITS_THRESHOLD(chan->rx.init_credits)) { goto done; } /* Restore credits */ credits = chan->rx.init_credits - k_sem_count_get(&chan->rx.credits); l2cap_chan_rx_give_credits(chan, credits); buf = l2cap_create_le_sig_pdu(BT_L2CAP_LE_CREDITS, get_ident(), sizeof(*ev)); ev = net_buf_add(buf, sizeof(*ev)); ev->cid = sys_cpu_to_le16(chan->rx.cid); ev->credits = sys_cpu_to_le16(credits); bt_l2cap_send(chan->chan.conn, BT_L2CAP_CID_LE_SIG, buf); done: BT_DBG("chan %p credits %u", chan, k_sem_count_get(&chan->rx.credits)); }
static void l2cap_send_reject(struct bt_conn *conn, uint8_t ident, uint16_t reason, void *data, uint8_t data_len) { struct bt_l2cap_cmd_reject *rej; struct bt_l2cap_sig_hdr *hdr; struct net_buf *buf; buf = bt_l2cap_create_pdu(&le_sig); if (!buf) { return; } hdr = net_buf_add(buf, sizeof(*hdr)); hdr->code = BT_L2CAP_CMD_REJECT; hdr->ident = ident; hdr->len = sys_cpu_to_le16(sizeof(*rej) + data_len); rej = net_buf_add(buf, sizeof(*rej)); rej->reason = sys_cpu_to_le16(reason); if (data) { memcpy(net_buf_add(buf, data_len), data, data_len); } bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf); }
int bt_l2cap_update_conn_param(struct bt_conn *conn, const struct bt_le_conn_param *param) { struct bt_l2cap_sig_hdr *hdr; struct bt_l2cap_conn_param_req *req; struct net_buf *buf; buf = bt_l2cap_create_pdu(&le_sig); if (!buf) { return -ENOBUFS; } hdr = net_buf_add(buf, sizeof(*hdr)); hdr->code = BT_L2CAP_CONN_PARAM_REQ; hdr->ident = get_ident(); hdr->len = sys_cpu_to_le16(sizeof(*req)); req = net_buf_add(buf, sizeof(*req)); req->min_interval = sys_cpu_to_le16(param->interval_min); req->max_interval = sys_cpu_to_le16(param->interval_max); req->latency = sys_cpu_to_le16(param->latency); req->timeout = sys_cpu_to_le16(param->timeout); bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf); return 0; }
static int l2cap_chan_le_send(struct bt_l2cap_le_chan *ch, struct net_buf *buf, uint16_t sdu_hdr_len) { int len; /* Wait for credits */ nano_sem_take(&ch->tx.credits, TICKS_UNLIMITED); buf = l2cap_chan_create_seg(ch, buf, sdu_hdr_len); if (!buf) { return -ENOMEM; } /* Channel may have been disconnected while waiting for credits */ if (!ch->chan.conn) { net_buf_unref(buf); return -ECONNRESET; } BT_DBG("ch %p cid 0x%04x len %u credits %u", ch, ch->tx.cid, buf->len, ch->tx.credits.nsig); len = buf->len; bt_l2cap_send(ch->chan.conn, ch->tx.cid, buf); return len; }
static uint8_t att_find_info_rsp(struct bt_att *att, uint16_t start_handle, uint16_t end_handle) { struct bt_conn *conn = att->chan.conn; struct find_info_data data; memset(&data, 0, sizeof(data)); data.buf = bt_att_create_pdu(conn, BT_ATT_OP_FIND_INFO_RSP, 0); if (!data.buf) { return BT_ATT_ERR_UNLIKELY; } data.att = att; bt_gatt_foreach_attr(start_handle, end_handle, find_info_cb, &data); if (!data.rsp) { net_buf_unref(data.buf); /* Respond since handle is set */ send_err_rsp(conn, BT_ATT_OP_FIND_INFO_REQ, start_handle, BT_ATT_ERR_ATTRIBUTE_NOT_FOUND); return 0; } bt_l2cap_send(conn, BT_L2CAP_CID_ATT, data.buf); return 0; }
static void bt_smp_recv(struct bt_l2cap_chan *chan, struct net_buf *buf) { struct bt_conn *conn = chan->conn; struct bt_smp_pairing_fail *rsp; struct bt_smp_hdr *hdr; /* If a device does not support pairing then it shall respond with * a Pairing Failed command with the reason set to “Pairing Not * Supported” when any command is received. * Core Specification Vol. 3, Part H, 3.3 */ buf = bt_l2cap_create_pdu(&smp_buf); if (!buf) { return; } hdr = net_buf_add(buf, sizeof(*hdr)); hdr->code = BT_SMP_CMD_PAIRING_FAIL; rsp = net_buf_add(buf, sizeof(*rsp)); rsp->reason = BT_SMP_ERR_PAIRING_NOTSUPP; bt_l2cap_send(conn, BT_L2CAP_CID_SMP, buf); }
static uint8_t att_exec_write_rsp(struct bt_conn *conn, uint8_t flags) { struct flush_data data; memset(&data, 0, sizeof(data)); data.buf = bt_att_create_pdu(conn, BT_ATT_OP_EXEC_WRITE_RSP, 0); if (!data.buf) { return BT_ATT_ERR_UNLIKELY; } data.conn = conn; data.flags = flags; /* Apply to the whole database */ bt_gatt_foreach_attr(0x0001, 0xffff, flush_cb, &data); /* In case of error discard data */ if (data.err) { net_buf_unref(data.buf); return data.err; } bt_l2cap_send(conn, BT_L2CAP_CID_ATT, data.buf); return 0; }
static void le_conn_param_update_req(struct bt_l2cap *l2cap, uint8_t ident, struct net_buf *buf) { struct bt_conn *conn = l2cap->chan.chan.conn; const struct bt_le_conn_param *param; uint16_t min, max, latency, timeout; bool params_valid; struct bt_l2cap_sig_hdr *hdr; struct bt_l2cap_conn_param_rsp *rsp; struct bt_l2cap_conn_param_req *req = (void *)buf->data; if (buf->len < sizeof(*req)) { BT_ERR("Too small LE conn update param req"); return; } if (conn->role != BT_HCI_ROLE_MASTER) { l2cap_send_reject(conn, ident, BT_L2CAP_REJ_NOT_UNDERSTOOD, NULL, 0); return; } min = sys_le16_to_cpu(req->min_interval); max = sys_le16_to_cpu(req->max_interval); latency = sys_le16_to_cpu(req->latency); timeout = sys_le16_to_cpu(req->timeout); param = BT_LE_CONN_PARAM(min, max, latency, timeout); BT_DBG("min 0x%4.4x max 0x%4.4x latency: 0x%4.4x timeout: 0x%4.4x", min, max, latency, timeout); buf = bt_l2cap_create_pdu(&le_sig); if (!buf) { return; } params_valid = bt_le_conn_params_valid(min, max, latency, timeout); hdr = net_buf_add(buf, sizeof(*hdr)); hdr->code = BT_L2CAP_CONN_PARAM_RSP; hdr->ident = ident; hdr->len = sys_cpu_to_le16(sizeof(*rsp)); rsp = net_buf_add(buf, sizeof(*rsp)); if (params_valid) { rsp->result = sys_cpu_to_le16(BT_L2CAP_CONN_PARAM_ACCEPTED); } else { rsp->result = sys_cpu_to_le16(BT_L2CAP_CONN_PARAM_REJECTED); } bt_l2cap_send(l2cap->chan.chan.conn, BT_L2CAP_CID_LE_SIG, buf); if (params_valid) { bt_conn_le_conn_update(conn, param); } }
static void le_disconn_req(struct bt_l2cap *l2cap, uint8_t ident, struct net_buf *buf) { struct bt_conn *conn = l2cap->chan.chan.conn; struct bt_l2cap_le_chan *chan; struct bt_l2cap_disconn_req *req = (void *)buf->data; struct bt_l2cap_disconn_rsp *rsp; struct bt_l2cap_sig_hdr *hdr; uint16_t scid, dcid; if (buf->len < sizeof(*req)) { BT_ERR("Too small LE conn req packet size"); return; } dcid = sys_le16_to_cpu(req->dcid); scid = sys_le16_to_cpu(req->scid); BT_DBG("scid 0x%04x dcid 0x%04x", dcid, scid); chan = l2cap_remove_tx_cid(conn, scid); if (!chan) { struct bt_l2cap_cmd_reject_cid_data data; data.scid = req->scid; data.dcid = req->dcid; l2cap_send_reject(conn, ident, BT_L2CAP_REJ_INVALID_CID, &data, sizeof(data)); return; } buf = bt_l2cap_create_pdu(&le_sig); if (!buf) { return; } hdr = net_buf_add(buf, sizeof(*hdr)); hdr->code = BT_L2CAP_DISCONN_RSP; hdr->ident = ident; hdr->len = sys_cpu_to_le16(sizeof(*rsp)); rsp = net_buf_add(buf, sizeof(*rsp)); rsp->dcid = sys_cpu_to_le16(chan->rx.cid); rsp->scid = sys_cpu_to_le16(chan->tx.cid); l2cap_chan_del(&chan->chan); bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf); }
static void le_conn_param_update_req(struct bt_l2cap *l2cap, uint8_t ident, struct net_buf *buf) { struct bt_conn *conn = l2cap->chan.chan.conn; struct bt_le_conn_param param; struct bt_l2cap_conn_param_rsp *rsp; struct bt_l2cap_conn_param_req *req = (void *)buf->data; bool accepted; if (buf->len < sizeof(*req)) { BT_ERR("Too small LE conn update param req"); return; } if (conn->role != BT_HCI_ROLE_MASTER) { l2cap_send_reject(conn, ident, BT_L2CAP_REJ_NOT_UNDERSTOOD, NULL, 0); return; } param.interval_min = sys_le16_to_cpu(req->min_interval); param.interval_max = sys_le16_to_cpu(req->max_interval); param.latency = sys_le16_to_cpu(req->latency); param.timeout = sys_le16_to_cpu(req->timeout); BT_DBG("min 0x%04x max 0x%04x latency: 0x%04x timeout: 0x%04x", param.interval_min, param.interval_max, param.latency, param.timeout); buf = l2cap_create_le_sig_pdu(BT_L2CAP_CONN_PARAM_RSP, ident, sizeof(*rsp)); accepted = le_param_req(conn, ¶m); rsp = net_buf_add(buf, sizeof(*rsp)); if (accepted) { rsp->result = sys_cpu_to_le16(BT_L2CAP_CONN_PARAM_ACCEPTED); } else { rsp->result = sys_cpu_to_le16(BT_L2CAP_CONN_PARAM_REJECTED); } bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf); if (accepted) { bt_conn_le_conn_update(conn, ¶m); } }
static uint8_t att_read_mult_req(struct bt_att *att, struct net_buf *buf) { struct bt_conn *conn = att->chan.conn; struct read_data data; uint16_t handle; memset(&data, 0, sizeof(data)); data.buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_MULT_RSP, 0); if (!data.buf) { return BT_ATT_ERR_UNLIKELY; } data.att = att; while (buf->len >= sizeof(uint16_t)) { handle = net_buf_pull_le16(buf); BT_DBG("handle 0x%04x ", handle); /* An Error Response shall be sent by the server in response to * the Read Multiple Request [....] if a read operation is not * permitted on any of the Characteristic Values. * * If handle is not valid then return invalid handle error. * If handle is found error will be cleared by read_cb. */ data.err = BT_ATT_ERR_INVALID_HANDLE; bt_gatt_foreach_attr(handle, handle, read_cb, &data); /* Stop reading in case of error */ if (data.err) { net_buf_unref(data.buf); /* Respond here since handle is set */ send_err_rsp(conn, BT_ATT_OP_READ_MULT_REQ, handle, data.err); return 0; } } bt_l2cap_send(conn, BT_L2CAP_CID_ATT, data.buf); return 0; }
int bt_att_send(struct bt_conn *conn, struct net_buf *buf, bt_att_func_t func, void *user_data, bt_att_destroy_t destroy) { struct bt_att *att; struct bt_att_hdr *hdr = (void *)buf->data; if (!conn) { return -EINVAL; } att = att_chan_get(conn); if (!att) { return -ENOTCONN; } if (func) { /* Check if there is a request pending */ if (att->req.func) { /* TODO: Allow more than one pending request */ return -EBUSY; } att->req.buf = net_buf_clone(buf); #if defined(CONFIG_BLUETOOTH_SMP) att->req.retrying = false; #endif /* CONFIG_BLUETOOTH_SMP */ att->req.func = func; att->req.user_data = user_data; att->req.destroy = destroy; } if (hdr->code == BT_ATT_OP_SIGNED_WRITE_CMD) { int err; err = bt_smp_sign(conn, buf); if (err) { BT_ERR("Error signing data"); return err; } } bt_l2cap_send(conn, BT_L2CAP_CID_ATT, buf); return 0; }
static void l2cap_send_reject(struct bt_conn *conn, uint8_t ident, uint16_t reason, void *data, uint8_t data_len) { struct bt_l2cap_cmd_reject *rej; struct net_buf *buf; buf = l2cap_create_le_sig_pdu(BT_L2CAP_CMD_REJECT, ident, sizeof(*rej) + data_len); rej = net_buf_add(buf, sizeof(*rej)); rej->reason = sys_cpu_to_le16(reason); if (data) { net_buf_add_mem(buf, data, data_len); } bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf); }
static uint8_t att_mtu_req(struct bt_att *att, struct net_buf *buf) { struct bt_conn *conn = att->chan.conn; struct bt_att_exchange_mtu_req *req; struct bt_att_exchange_mtu_rsp *rsp; struct net_buf *pdu; uint16_t mtu_client, mtu_server; req = (void *)buf->data; mtu_client = sys_le16_to_cpu(req->mtu); BT_DBG("Client MTU %u", mtu_client); /* Check if MTU is valid */ if (mtu_client < BT_ATT_DEFAULT_LE_MTU) { return BT_ATT_ERR_INVALID_PDU; } pdu = bt_att_create_pdu(conn, BT_ATT_OP_MTU_RSP, sizeof(*rsp)); if (!pdu) { return BT_ATT_ERR_UNLIKELY; } mtu_server = CONFIG_BLUETOOTH_ATT_MTU; BT_DBG("Server MTU %u", mtu_server); rsp = net_buf_add(pdu, sizeof(*rsp)); rsp->mtu = sys_cpu_to_le16(mtu_server); bt_l2cap_send(conn, BT_L2CAP_CID_ATT, pdu); /* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part F] page 484: * * A device's Exchange MTU Request shall contain the same MTU as the * device's Exchange MTU Response (i.e. the MTU shall be symmetric). */ att->chan.rx.mtu = min(mtu_client, mtu_server); att->chan.tx.mtu = att->chan.rx.mtu; BT_DBG("Negotiated MTU %u", att->chan.rx.mtu); return 0; }
int bt_l2cap_update_conn_param(struct bt_conn *conn, const struct bt_le_conn_param *param) { struct bt_l2cap_conn_param_req *req; struct net_buf *buf; buf = l2cap_create_le_sig_pdu(BT_L2CAP_CONN_PARAM_REQ, get_ident(), sizeof(*req)); req = net_buf_add(buf, sizeof(*req)); req->min_interval = sys_cpu_to_le16(param->interval_min); req->max_interval = sys_cpu_to_le16(param->interval_max); req->latency = sys_cpu_to_le16(param->latency); req->timeout = sys_cpu_to_le16(param->timeout); bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf); return 0; }
static int l2cap_le_connect(struct bt_conn *conn, struct bt_l2cap_le_chan *ch, uint16_t psm) { struct net_buf *buf; struct bt_l2cap_sig_hdr *hdr; struct bt_l2cap_le_conn_req *req; if (psm < L2CAP_LE_PSM_START || psm > L2CAP_LE_PSM_END) { return -EINVAL; } l2cap_chan_tx_init(ch); l2cap_chan_rx_init(ch); if (!l2cap_chan_add(conn, &ch->chan)) { return -ENOMEM; } buf = bt_l2cap_create_pdu(&le_sig); if (!buf) { BT_ERR("Unable to send L2CAP connection request"); return -ENOMEM; } ch->ident = get_ident(); hdr = net_buf_add(buf, sizeof(*hdr)); hdr->code = BT_L2CAP_LE_CONN_REQ; hdr->ident = ch->ident; hdr->len = sys_cpu_to_le16(sizeof(*req)); req = net_buf_add(buf, sizeof(*req)); req->psm = sys_cpu_to_le16(psm); req->scid = sys_cpu_to_le16(ch->rx.cid); req->mtu = sys_cpu_to_le16(ch->rx.mtu); req->mps = sys_cpu_to_le16(ch->rx.mps); req->credits = sys_cpu_to_le16(L2CAP_LE_MAX_CREDITS); bt_l2cap_send(ch->chan.conn, BT_L2CAP_CID_LE_SIG, buf); return 0; }
int bt_l2cap_chan_disconnect(struct bt_l2cap_chan *chan) { struct bt_conn *conn = chan->conn; struct net_buf *buf; struct bt_l2cap_disconn_req *req; struct bt_l2cap_sig_hdr *hdr; struct bt_l2cap_le_chan *ch; if (!conn) { return -ENOTCONN; } #if defined(CONFIG_BLUETOOTH_BREDR) if (conn->type == BT_CONN_TYPE_BR) { return bt_l2cap_br_chan_disconnect(chan); } #endif /* CONFIG_BLUETOOTH_BREDR */ ch = LE_CHAN(chan); BT_DBG("chan %p scid 0x%04x dcid 0x%04x", chan, ch->rx.cid, ch->tx.cid); buf = bt_l2cap_create_pdu(&le_sig); if (!buf) { BT_ERR("Unable to send L2CP disconnect request"); return -ENOMEM; } hdr = net_buf_add(buf, sizeof(*hdr)); hdr->code = BT_L2CAP_DISCONN_REQ; hdr->ident = get_ident(); hdr->len = sys_cpu_to_le16(sizeof(*req)); req = net_buf_add(buf, sizeof(*req)); req->dcid = sys_cpu_to_le16(ch->tx.cid); req->scid = sys_cpu_to_le16(ch->rx.cid); bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf); return 0; }
static uint8_t att_indicate(struct bt_att *att, struct net_buf *buf) { struct bt_conn *conn = att->chan.conn; uint16_t handle; handle = net_buf_pull_le16(buf); BT_DBG("handle 0x%04x", handle); bt_gatt_notification(conn, handle, buf->data, buf->len); buf = bt_att_create_pdu(conn, BT_ATT_OP_CONFIRM, 0); if (!buf) { return 0; } bt_l2cap_send(conn, BT_L2CAP_CID_ATT, buf); return 0; }
static void l2cap_chan_send_req(struct bt_l2cap_le_chan *chan, struct net_buf *buf, int32_t timeout) { /* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part A] page 126: * * The value of this timer is implementation-dependent but the minimum * initial value is 1 second and the maximum initial value is 60 * seconds. One RTX timer shall exist for each outstanding signaling * request, including each Echo Request. The timer disappears on the * final expiration, when the response is received, or the physical * link is lost. */ if (timeout) { k_delayed_work_submit(&chan->chan.rtx_work, timeout); } else { k_delayed_work_cancel(&chan->chan.rtx_work); } bt_l2cap_send(chan->chan.conn, BT_L2CAP_CID_LE_SIG, buf); }
static void send_err_rsp(struct bt_conn *conn, uint8_t req, uint16_t handle, uint8_t err) { struct bt_att_error_rsp *rsp; struct net_buf *buf; /* Ignore opcode 0x00 */ if (!req) { return; } buf = bt_att_create_pdu(conn, BT_ATT_OP_ERROR_RSP, sizeof(*rsp)); if (!buf) { return; } rsp = net_buf_add(buf, sizeof(*rsp)); rsp->request = req; rsp->handle = sys_cpu_to_le16(handle); rsp->error = err; bt_l2cap_send(conn, BT_L2CAP_CID_ATT, buf); }
static uint8_t att_find_type_rsp(struct bt_att *att, uint16_t start_handle, uint16_t end_handle, const void *value, uint8_t value_len) { struct bt_conn *conn = att->chan.conn; struct find_type_data data; memset(&data, 0, sizeof(data)); data.buf = bt_att_create_pdu(conn, BT_ATT_OP_FIND_TYPE_RSP, 0); if (!data.buf) { return BT_ATT_ERR_UNLIKELY; } data.att = att; data.group = NULL; data.value = value; data.value_len = value_len; /* Pre-set error in case no service will be found */ data.err = BT_ATT_ERR_ATTRIBUTE_NOT_FOUND; bt_gatt_foreach_attr(start_handle, end_handle, find_type_cb, &data); /* If error has not been cleared, no service has been found */ if (data.err) { net_buf_unref(data.buf); /* Respond since handle is set */ send_err_rsp(conn, BT_ATT_OP_FIND_TYPE_REQ, start_handle, data.err); return 0; } bt_l2cap_send(conn, BT_L2CAP_CID_ATT, data.buf); return 0; }
static uint8_t att_read_rsp(struct bt_att *att, uint8_t op, uint8_t rsp, uint16_t handle, uint16_t offset) { struct bt_conn *conn = att->chan.conn; struct read_data data; if (!handle) { return BT_ATT_ERR_INVALID_HANDLE; } memset(&data, 0, sizeof(data)); data.buf = bt_att_create_pdu(conn, rsp, 0); if (!data.buf) { return BT_ATT_ERR_UNLIKELY; } data.att = att; data.offset = offset; /* Pre-set error if no attr will be found in handle */ data.err = BT_ATT_ERR_INVALID_HANDLE; bt_gatt_foreach_attr(handle, handle, read_cb, &data); /* In case of error discard data and respond with an error */ if (data.err) { net_buf_unref(data.buf); /* Respond here since handle is set */ send_err_rsp(conn, op, handle, data.err); return 0; } bt_l2cap_send(conn, BT_L2CAP_CID_ATT, data.buf); return 0; }
static void bt_att_encrypt_change(struct bt_l2cap_chan *chan) { struct bt_att *att = CONTAINER_OF(chan, struct bt_att, chan); struct bt_conn *conn = chan->conn; struct bt_att_req *req; BT_DBG("chan %p conn %p handle %u sec_level 0x%02x", chan, conn, conn->handle, conn->sec_level); if (conn->sec_level == BT_SECURITY_LOW) { return; } req = &att->req; if (!req->retrying) { return; } BT_DBG("Retrying"); /* Resend buffer */ bt_l2cap_send(conn, BT_L2CAP_CID_ATT, req->buf); req->buf = NULL; }
static uint8_t att_read_type_rsp(struct bt_att *att, struct bt_uuid *uuid, uint16_t start_handle, uint16_t end_handle) { struct bt_conn *conn = att->chan.conn; struct read_type_data data; memset(&data, 0, sizeof(data)); data.buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_TYPE_RSP, sizeof(*data.rsp)); if (!data.buf) { return BT_ATT_ERR_UNLIKELY; } data.att = att; data.uuid = uuid; data.rsp = net_buf_add(data.buf, sizeof(*data.rsp)); data.rsp->len = 0; /* Pre-set error if no attr will be found in handle */ data.err = BT_ATT_ERR_ATTRIBUTE_NOT_FOUND; bt_gatt_foreach_attr(start_handle, end_handle, read_type_cb, &data); if (data.err) { net_buf_unref(data.buf); /* Response here since handle is set */ send_err_rsp(conn, BT_ATT_OP_READ_TYPE_REQ, start_handle, data.err); return 0; } bt_l2cap_send(conn, BT_L2CAP_CID_ATT, data.buf); return 0; }
static void l2cap_chan_update_credits(struct bt_l2cap_le_chan *chan) { struct net_buf *buf; struct bt_l2cap_sig_hdr *hdr; struct bt_l2cap_le_credits *ev; uint16_t credits; /* Only give more credits if it went bellow the defined threshold */ if (chan->rx.credits.nsig > L2CAP_LE_CREDITS_THRESHOLD) { goto done; } /* Restore credits */ credits = L2CAP_LE_MAX_CREDITS - chan->rx.credits.nsig; l2cap_chan_rx_give_credits(chan, credits); buf = bt_l2cap_create_pdu(&le_sig); if (!buf) { BT_ERR("Unable to send credits"); return; } hdr = net_buf_add(buf, sizeof(*hdr)); hdr->code = BT_L2CAP_LE_CREDITS; hdr->ident = get_ident(); hdr->len = sys_cpu_to_le16(sizeof(*ev)); ev = net_buf_add(buf, sizeof(*ev)); ev->cid = sys_cpu_to_le16(chan->rx.cid); ev->credits = sys_cpu_to_le16(credits); bt_l2cap_send(chan->chan.conn, BT_L2CAP_CID_LE_SIG, buf); done: BT_DBG("chan %p credits %u", chan, chan->rx.credits.nsig); }
static int att_notify(struct bt_conn *conn, uint16_t handle, const void *data, size_t len) { struct net_buf *buf; struct bt_att_notify *nfy; buf = bt_att_create_pdu(conn, BT_ATT_OP_NOTIFY, sizeof(*nfy) + len); if (!buf) { BT_WARN("No buffer available to send notification"); return -ENOMEM; } BT_DBG("conn %p handle 0x%04x", conn, handle); nfy = net_buf_add(buf, sizeof(*nfy)); nfy->handle = sys_cpu_to_le16(handle); net_buf_add(buf, len); memcpy(nfy->value, data, len); bt_l2cap_send(conn, BT_L2CAP_CID_ATT, buf); return 0; }
static void le_conn_req(struct bt_l2cap *l2cap, uint8_t ident, struct net_buf *buf) { struct bt_conn *conn = l2cap->chan.chan.conn; struct bt_l2cap_chan *chan; struct bt_l2cap_server *server; struct bt_l2cap_le_conn_req *req = (void *)buf->data; struct bt_l2cap_le_conn_rsp *rsp; struct bt_l2cap_sig_hdr *hdr; uint16_t psm, scid, mtu, mps, credits; if (buf->len < sizeof(*req)) { BT_ERR("Too small LE conn req packet size"); return; } psm = sys_le16_to_cpu(req->psm); scid = sys_le16_to_cpu(req->scid); mtu = sys_le16_to_cpu(req->mtu); mps = sys_le16_to_cpu(req->mps); credits = sys_le16_to_cpu(req->credits); BT_DBG("psm 0x%02x scid 0x%04x mtu %u mps %u credits %u", psm, scid, mtu, mps, credits); if (mtu < L2CAP_LE_MIN_MTU || mps < L2CAP_LE_MIN_MTU) { BT_ERR("Invalid LE-Conn Req params"); return; } buf = bt_l2cap_create_pdu(&le_sig); if (!buf) { return; } hdr = net_buf_add(buf, sizeof(*hdr)); hdr->code = BT_L2CAP_LE_CONN_RSP; hdr->ident = ident; hdr->len = sys_cpu_to_le16(sizeof(*rsp)); rsp = net_buf_add(buf, sizeof(*rsp)); memset(rsp, 0, sizeof(*rsp)); /* Check if there is a server registered */ server = l2cap_server_lookup_psm(psm); if (!server) { rsp->result = sys_cpu_to_le16(BT_L2CAP_ERR_PSM_NOT_SUPP); goto rsp; } /* TODO: Add security check */ if (scid < L2CAP_LE_DYN_CID_START || scid > L2CAP_LE_DYN_CID_END) { rsp->result = sys_cpu_to_le16(BT_L2CAP_ERR_INVALID_SCID); goto rsp; } chan = bt_l2cap_le_lookup_tx_cid(conn, scid); if (chan) { rsp->result = sys_cpu_to_le16(BT_L2CAP_ERR_SCID_IN_USE); goto rsp; } /* Request server to accept the new connection and allocate the * channel. * * TODO: Handle different errors, it may be required to respond async. */ if (server->accept(conn, &chan) < 0) { rsp->result = sys_cpu_to_le16(BT_L2CAP_ERR_NO_RESOURCES); goto rsp; } if (l2cap_chan_add(conn, chan)) { struct bt_l2cap_le_chan *ch = LE_CHAN(chan); /* Init TX parameters */ l2cap_chan_tx_init(ch); ch->tx.cid = scid; ch->tx.mps = mps; ch->tx.mtu = mtu; l2cap_chan_tx_give_credits(ch, credits); /* Init RX parameters */ l2cap_chan_rx_init(ch); l2cap_chan_rx_give_credits(ch, L2CAP_LE_MAX_CREDITS); if (chan->ops && chan->ops->connected) { chan->ops->connected(chan); } /* Prepare response protocol data */ rsp->dcid = sys_cpu_to_le16(ch->rx.cid); rsp->mps = sys_cpu_to_le16(ch->rx.mps); rsp->mtu = sys_cpu_to_le16(ch->rx.mtu); rsp->credits = sys_cpu_to_le16(L2CAP_LE_MAX_CREDITS); rsp->result = BT_L2CAP_SUCCESS; } else { rsp->result = sys_cpu_to_le16(BT_L2CAP_ERR_NO_RESOURCES); } rsp: bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf); }
static uint8_t att_write_rsp(struct bt_conn *conn, uint8_t op, uint8_t rsp, uint16_t handle, uint16_t offset, const void *value, uint8_t len) { struct write_data data; if (!handle) { return BT_ATT_ERR_INVALID_HANDLE; } memset(&data, 0, sizeof(data)); /* Only allocate buf if required to respond */ if (rsp) { data.buf = bt_att_create_pdu(conn, rsp, 0); if (!data.buf) { return BT_ATT_ERR_UNLIKELY; } } data.conn = conn; data.op = op; data.offset = offset; data.value = value; data.len = len; data.err = BT_ATT_ERR_INVALID_HANDLE; bt_gatt_foreach_attr(handle, handle, write_cb, &data); if (data.err) { /* Don't send error response when user attribute write handler * returns invalid offset or invalid attribute value length. * Such response needs to be sent when execute write request * is received. * Refer to BT SIG 4.2 [Vol 3, Part F, 3.4.6.1] page 504 */ if (data.op == BT_ATT_OP_PREPARE_WRITE_REQ && (data.err == BT_ATT_ERR_INVALID_OFFSET || data.err == BT_ATT_ERR_INVALID_ATTRIBUTE_LEN)) { goto done; } /* In case of error discard data and respond with an error */ if (rsp) { net_buf_unref(data.buf); /* Respond here since handle is set */ send_err_rsp(conn, op, handle, data.err); } return 0; } done: if (data.buf) { /* Add prepare write response */ if (rsp == BT_ATT_OP_PREPARE_WRITE_RSP) { struct bt_att_prepare_write_rsp *rsp; rsp = net_buf_add(data.buf, sizeof(*rsp)); rsp->handle = sys_cpu_to_le16(handle); rsp->offset = sys_cpu_to_le16(offset); net_buf_add(data.buf, len); memcpy(rsp->value, value, len); } bt_l2cap_send(conn, BT_L2CAP_CID_ATT, data.buf); } return 0; }