/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #include #include #include "ble_hs_priv.h" static uint16_t ble_att_preferred_mtu_val; /** Dispatch table for incoming ATT requests. Sorted by op code. */ typedef int ble_att_rx_fn(uint16_t conn_handle, struct os_mbuf **om); struct ble_att_rx_dispatch_entry { uint8_t bde_op; ble_att_rx_fn *bde_fn; }; /** Dispatch table for incoming ATT commands. Must be ordered by op code. */ static const struct ble_att_rx_dispatch_entry ble_att_rx_dispatch[] = { { BLE_ATT_OP_ERROR_RSP, ble_att_clt_rx_error }, { BLE_ATT_OP_MTU_REQ, ble_att_svr_rx_mtu }, { BLE_ATT_OP_MTU_RSP, ble_att_clt_rx_mtu }, { BLE_ATT_OP_FIND_INFO_REQ, ble_att_svr_rx_find_info }, { BLE_ATT_OP_FIND_INFO_RSP, ble_att_clt_rx_find_info }, { BLE_ATT_OP_FIND_TYPE_VALUE_REQ, ble_att_svr_rx_find_type_value }, { BLE_ATT_OP_FIND_TYPE_VALUE_RSP, ble_att_clt_rx_find_type_value }, { BLE_ATT_OP_READ_TYPE_REQ, ble_att_svr_rx_read_type }, { BLE_ATT_OP_READ_TYPE_RSP, ble_att_clt_rx_read_type }, { BLE_ATT_OP_READ_REQ, ble_att_svr_rx_read }, { BLE_ATT_OP_READ_RSP, ble_att_clt_rx_read }, { BLE_ATT_OP_READ_BLOB_REQ, ble_att_svr_rx_read_blob }, { BLE_ATT_OP_READ_BLOB_RSP, ble_att_clt_rx_read_blob }, { BLE_ATT_OP_READ_MULT_REQ, ble_att_svr_rx_read_mult }, { BLE_ATT_OP_READ_MULT_RSP, ble_att_clt_rx_read_mult }, { BLE_ATT_OP_READ_GROUP_TYPE_REQ, ble_att_svr_rx_read_group_type }, { BLE_ATT_OP_READ_GROUP_TYPE_RSP, ble_att_clt_rx_read_group_type }, { BLE_ATT_OP_WRITE_REQ, ble_att_svr_rx_write }, { BLE_ATT_OP_WRITE_RSP, ble_att_clt_rx_write }, { BLE_ATT_OP_PREP_WRITE_REQ, ble_att_svr_rx_prep_write }, { BLE_ATT_OP_PREP_WRITE_RSP, ble_att_clt_rx_prep_write }, { BLE_ATT_OP_EXEC_WRITE_REQ, ble_att_svr_rx_exec_write }, { BLE_ATT_OP_EXEC_WRITE_RSP, ble_att_clt_rx_exec_write }, { BLE_ATT_OP_NOTIFY_REQ, ble_att_svr_rx_notify }, { BLE_ATT_OP_INDICATE_REQ, ble_att_svr_rx_indicate }, { BLE_ATT_OP_INDICATE_RSP, ble_att_clt_rx_indicate }, { BLE_ATT_OP_WRITE_CMD, ble_att_svr_rx_write_no_rsp }, }; #define BLE_ATT_RX_DISPATCH_SZ \ (sizeof ble_att_rx_dispatch / sizeof ble_att_rx_dispatch[0]) STATS_SECT_DECL(ble_att_stats) ble_att_stats; STATS_NAME_START(ble_att_stats) STATS_NAME(ble_att_stats, error_rsp_rx) STATS_NAME(ble_att_stats, error_rsp_tx) STATS_NAME(ble_att_stats, mtu_req_rx) STATS_NAME(ble_att_stats, mtu_req_tx) STATS_NAME(ble_att_stats, mtu_rsp_rx) STATS_NAME(ble_att_stats, mtu_rsp_tx) STATS_NAME(ble_att_stats, find_info_req_rx) STATS_NAME(ble_att_stats, find_info_req_tx) STATS_NAME(ble_att_stats, find_info_rsp_rx) STATS_NAME(ble_att_stats, find_info_rsp_tx) STATS_NAME(ble_att_stats, find_type_value_req_rx) STATS_NAME(ble_att_stats, find_type_value_req_tx) STATS_NAME(ble_att_stats, find_type_value_rsp_rx) STATS_NAME(ble_att_stats, find_type_value_rsp_tx) STATS_NAME(ble_att_stats, read_type_req_rx) STATS_NAME(ble_att_stats, read_type_req_tx) STATS_NAME(ble_att_stats, read_type_rsp_rx) STATS_NAME(ble_att_stats, read_type_rsp_tx) STATS_NAME(ble_att_stats, read_req_rx) STATS_NAME(ble_att_stats, read_req_tx) STATS_NAME(ble_att_stats, read_rsp_rx) STATS_NAME(ble_att_stats, read_rsp_tx) STATS_NAME(ble_att_stats, read_blob_req_rx) STATS_NAME(ble_att_stats, read_blob_req_tx) STATS_NAME(ble_att_stats, read_blob_rsp_rx) STATS_NAME(ble_att_stats, read_blob_rsp_tx) STATS_NAME(ble_att_stats, read_mult_req_rx) STATS_NAME(ble_att_stats, read_mult_req_tx) STATS_NAME(ble_att_stats, read_mult_rsp_rx) STATS_NAME(ble_att_stats, read_mult_rsp_tx) STATS_NAME(ble_att_stats, read_group_type_req_rx) STATS_NAME(ble_att_stats, read_group_type_req_tx) STATS_NAME(ble_att_stats, read_group_type_rsp_rx) STATS_NAME(ble_att_stats, read_group_type_rsp_tx) STATS_NAME(ble_att_stats, write_req_rx) STATS_NAME(ble_att_stats, write_req_tx) STATS_NAME(ble_att_stats, write_rsp_rx) STATS_NAME(ble_att_stats, write_rsp_tx) STATS_NAME(ble_att_stats, prep_write_req_rx) STATS_NAME(ble_att_stats, prep_write_req_tx) STATS_NAME(ble_att_stats, prep_write_rsp_rx) STATS_NAME(ble_att_stats, prep_write_rsp_tx) STATS_NAME(ble_att_stats, exec_write_req_rx) STATS_NAME(ble_att_stats, exec_write_req_tx) STATS_NAME(ble_att_stats, exec_write_rsp_rx) STATS_NAME(ble_att_stats, exec_write_rsp_tx) STATS_NAME(ble_att_stats, notify_req_rx) STATS_NAME(ble_att_stats, notify_req_tx) STATS_NAME(ble_att_stats, indicate_req_rx) STATS_NAME(ble_att_stats, indicate_req_tx) STATS_NAME(ble_att_stats, indicate_rsp_rx) STATS_NAME(ble_att_stats, indicate_rsp_tx) STATS_NAME(ble_att_stats, write_cmd_rx) STATS_NAME(ble_att_stats, write_cmd_tx) STATS_NAME_END(ble_att_stats) static const struct ble_att_rx_dispatch_entry * ble_att_rx_dispatch_entry_find(uint8_t op) { const struct ble_att_rx_dispatch_entry *entry; int i; for (i = 0; i < BLE_ATT_RX_DISPATCH_SZ; i++) { entry = ble_att_rx_dispatch + i; if (entry->bde_op == op) { return entry; } if (entry->bde_op > op) { break; } } return NULL; } int ble_att_conn_chan_find(uint16_t conn_handle, struct ble_hs_conn **out_conn, struct ble_l2cap_chan **out_chan) { return ble_hs_misc_conn_chan_find(conn_handle, BLE_L2CAP_CID_ATT, out_conn, out_chan); } void ble_att_inc_tx_stat(uint8_t att_op) { switch (att_op) { case BLE_ATT_OP_ERROR_RSP: STATS_INC(ble_att_stats, error_rsp_tx); break; case BLE_ATT_OP_MTU_REQ: STATS_INC(ble_att_stats, mtu_req_tx); break; case BLE_ATT_OP_MTU_RSP: STATS_INC(ble_att_stats, mtu_rsp_tx); break; case BLE_ATT_OP_FIND_INFO_REQ: STATS_INC(ble_att_stats, find_info_req_tx); break; case BLE_ATT_OP_FIND_INFO_RSP: STATS_INC(ble_att_stats, find_info_rsp_tx); break; case BLE_ATT_OP_FIND_TYPE_VALUE_REQ: STATS_INC(ble_att_stats, find_type_value_req_tx); break; case BLE_ATT_OP_FIND_TYPE_VALUE_RSP: STATS_INC(ble_att_stats, find_type_value_rsp_tx); break; case BLE_ATT_OP_READ_TYPE_REQ: STATS_INC(ble_att_stats, read_type_req_tx); break; case BLE_ATT_OP_READ_TYPE_RSP: STATS_INC(ble_att_stats, read_type_rsp_tx); break; case BLE_ATT_OP_READ_REQ: STATS_INC(ble_att_stats, read_req_tx); break; case BLE_ATT_OP_READ_RSP: STATS_INC(ble_att_stats, read_rsp_tx); break; case BLE_ATT_OP_READ_BLOB_REQ: STATS_INC(ble_att_stats, read_blob_req_tx); break; case BLE_ATT_OP_READ_BLOB_RSP: STATS_INC(ble_att_stats, read_blob_rsp_tx); break; case BLE_ATT_OP_READ_MULT_REQ: STATS_INC(ble_att_stats, read_mult_req_tx); break; case BLE_ATT_OP_READ_MULT_RSP: STATS_INC(ble_att_stats, read_mult_rsp_tx); break; case BLE_ATT_OP_READ_GROUP_TYPE_REQ: STATS_INC(ble_att_stats, read_group_type_req_tx); break; case BLE_ATT_OP_READ_GROUP_TYPE_RSP: STATS_INC(ble_att_stats, read_group_type_rsp_tx); break; case BLE_ATT_OP_WRITE_REQ: STATS_INC(ble_att_stats, write_req_tx); break; case BLE_ATT_OP_WRITE_RSP: STATS_INC(ble_att_stats, write_rsp_tx); break; case BLE_ATT_OP_PREP_WRITE_REQ: STATS_INC(ble_att_stats, prep_write_req_tx); break; case BLE_ATT_OP_PREP_WRITE_RSP: STATS_INC(ble_att_stats, prep_write_rsp_tx); break; case BLE_ATT_OP_EXEC_WRITE_REQ: STATS_INC(ble_att_stats, exec_write_req_tx); break; case BLE_ATT_OP_EXEC_WRITE_RSP: STATS_INC(ble_att_stats, exec_write_rsp_tx); break; case BLE_ATT_OP_NOTIFY_REQ: STATS_INC(ble_att_stats, notify_req_tx); break; case BLE_ATT_OP_INDICATE_REQ: STATS_INC(ble_att_stats, indicate_req_tx); break; case BLE_ATT_OP_INDICATE_RSP: STATS_INC(ble_att_stats, indicate_rsp_tx); break; case BLE_ATT_OP_WRITE_CMD: STATS_INC(ble_att_stats, write_cmd_tx); break; default: break; } } static void ble_att_inc_rx_stat(uint8_t att_op) { switch (att_op) { case BLE_ATT_OP_ERROR_RSP: STATS_INC(ble_att_stats, error_rsp_rx); break; case BLE_ATT_OP_MTU_REQ: STATS_INC(ble_att_stats, mtu_req_rx); break; case BLE_ATT_OP_MTU_RSP: STATS_INC(ble_att_stats, mtu_rsp_rx); break; case BLE_ATT_OP_FIND_INFO_REQ: STATS_INC(ble_att_stats, find_info_req_rx); break; case BLE_ATT_OP_FIND_INFO_RSP: STATS_INC(ble_att_stats, find_info_rsp_rx); break; case BLE_ATT_OP_FIND_TYPE_VALUE_REQ: STATS_INC(ble_att_stats, find_type_value_req_rx); break; case BLE_ATT_OP_FIND_TYPE_VALUE_RSP: STATS_INC(ble_att_stats, find_type_value_rsp_rx); break; case BLE_ATT_OP_READ_TYPE_REQ: STATS_INC(ble_att_stats, read_type_req_rx); break; case BLE_ATT_OP_READ_TYPE_RSP: STATS_INC(ble_att_stats, read_type_rsp_rx); break; case BLE_ATT_OP_READ_REQ: STATS_INC(ble_att_stats, read_req_rx); break; case BLE_ATT_OP_READ_RSP: STATS_INC(ble_att_stats, read_rsp_rx); break; case BLE_ATT_OP_READ_BLOB_REQ: STATS_INC(ble_att_stats, read_blob_req_rx); break; case BLE_ATT_OP_READ_BLOB_RSP: STATS_INC(ble_att_stats, read_blob_rsp_rx); break; case BLE_ATT_OP_READ_MULT_REQ: STATS_INC(ble_att_stats, read_mult_req_rx); break; case BLE_ATT_OP_READ_MULT_RSP: STATS_INC(ble_att_stats, read_mult_rsp_rx); break; case BLE_ATT_OP_READ_GROUP_TYPE_REQ: STATS_INC(ble_att_stats, read_group_type_req_rx); break; case BLE_ATT_OP_READ_GROUP_TYPE_RSP: STATS_INC(ble_att_stats, read_group_type_rsp_rx); break; case BLE_ATT_OP_WRITE_REQ: STATS_INC(ble_att_stats, write_req_rx); break; case BLE_ATT_OP_WRITE_RSP: STATS_INC(ble_att_stats, write_rsp_rx); break; case BLE_ATT_OP_PREP_WRITE_REQ: STATS_INC(ble_att_stats, prep_write_req_rx); break; case BLE_ATT_OP_PREP_WRITE_RSP: STATS_INC(ble_att_stats, prep_write_rsp_rx); break; case BLE_ATT_OP_EXEC_WRITE_REQ: STATS_INC(ble_att_stats, exec_write_req_rx); break; case BLE_ATT_OP_EXEC_WRITE_RSP: STATS_INC(ble_att_stats, exec_write_rsp_rx); break; case BLE_ATT_OP_NOTIFY_REQ: STATS_INC(ble_att_stats, notify_req_rx); break; case BLE_ATT_OP_INDICATE_REQ: STATS_INC(ble_att_stats, indicate_req_rx); break; case BLE_ATT_OP_INDICATE_RSP: STATS_INC(ble_att_stats, indicate_rsp_rx); break; case BLE_ATT_OP_WRITE_CMD: STATS_INC(ble_att_stats, write_cmd_rx); break; default: break; } } void ble_att_truncate_to_mtu(const struct ble_l2cap_chan *att_chan, struct os_mbuf *txom) { int32_t extra_len; uint16_t mtu; mtu = ble_att_chan_mtu(att_chan); extra_len = OS_MBUF_PKTLEN(txom) - mtu; if (extra_len > 0) { os_mbuf_adj(txom, -extra_len); } } uint16_t ble_att_mtu(uint16_t conn_handle) { struct ble_l2cap_chan *chan; struct ble_hs_conn *conn; uint16_t mtu; int rc; ble_hs_lock(); rc = ble_att_conn_chan_find(conn_handle, &conn, &chan); if (rc == 0) { mtu = ble_att_chan_mtu(chan); } else { mtu = 0; } ble_hs_unlock(); return mtu; } void ble_att_set_peer_mtu(struct ble_l2cap_chan *chan, uint16_t peer_mtu) { if (peer_mtu < BLE_ATT_MTU_DFLT) { peer_mtu = BLE_ATT_MTU_DFLT; } chan->peer_mtu = peer_mtu; } uint16_t ble_att_chan_mtu(const struct ble_l2cap_chan *chan) { uint16_t mtu; /* If either side has not exchanged MTU size, use the default. Otherwise, * use the lesser of the two exchanged values. */ if (!(ble_l2cap_is_mtu_req_sent(chan)) || chan->peer_mtu == 0) { mtu = BLE_ATT_MTU_DFLT; } else { mtu = min(chan->my_mtu, chan->peer_mtu); } BLE_HS_DBG_ASSERT(mtu >= BLE_ATT_MTU_DFLT); return mtu; } static void ble_att_rx_handle_unknown_request(uint8_t op, uint16_t conn_handle, struct os_mbuf **om) { /* If this is command (bit6 is set to 1), do nothing */ if (op & 0x40) { return; } os_mbuf_adj(*om, OS_MBUF_PKTLEN(*om)); ble_att_svr_tx_error_rsp(conn_handle, *om, op, 0, BLE_ATT_ERR_REQ_NOT_SUPPORTED); *om = NULL; } static int ble_att_rx(struct ble_l2cap_chan *chan) { const struct ble_att_rx_dispatch_entry *entry; uint8_t op; uint16_t conn_handle; struct os_mbuf **om; int rc; conn_handle = ble_l2cap_get_conn_handle(chan); if (conn_handle == BLE_HS_CONN_HANDLE_NONE) { return BLE_HS_ENOTCONN; } om = &chan->rx_buf; BLE_HS_DBG_ASSERT(*om != NULL); rc = os_mbuf_copydata(*om, 0, 1, &op); if (rc != 0) { return BLE_HS_EMSGSIZE; } entry = ble_att_rx_dispatch_entry_find(op); if (entry == NULL) { ble_att_rx_handle_unknown_request(op, conn_handle, om); return BLE_HS_ENOTSUP; } ble_att_inc_rx_stat(op); /* Strip L2CAP ATT header from the front of the mbuf. */ os_mbuf_adj(*om, 1); rc = entry->bde_fn(conn_handle, om); if (rc != 0) { if (rc == BLE_HS_ENOTSUP) { ble_att_rx_handle_unknown_request(op, conn_handle, om); } return rc; } return 0; } uint16_t ble_att_preferred_mtu(void) { return ble_att_preferred_mtu_val; } int ble_att_set_preferred_mtu(uint16_t mtu) { struct ble_l2cap_chan *chan; struct ble_hs_conn *conn; int i; if (mtu < BLE_ATT_MTU_DFLT) { return BLE_HS_EINVAL; } if (mtu > BLE_ATT_MTU_MAX) { return BLE_HS_EINVAL; } ble_att_preferred_mtu_val = mtu; /* Set my_mtu for established connections that haven't exchanged. */ ble_hs_lock(); i = 0; while ((conn = ble_hs_conn_find_by_idx(i)) != NULL) { chan = ble_hs_conn_chan_find_by_scid(conn, BLE_L2CAP_CID_ATT); BLE_HS_DBG_ASSERT(chan != NULL); if (!(chan->flags & BLE_L2CAP_CHAN_F_TXED_MTU)) { chan->my_mtu = mtu; } i++; } ble_hs_unlock(); return 0; } struct ble_l2cap_chan * ble_att_create_chan(uint16_t conn_handle) { struct ble_l2cap_chan *chan; chan = ble_l2cap_chan_alloc(conn_handle); if (chan == NULL) { return NULL; } chan->scid = BLE_L2CAP_CID_ATT; chan->dcid = BLE_L2CAP_CID_ATT; chan->my_mtu = ble_att_preferred_mtu_val; chan->rx_fn = ble_att_rx; return chan; } int ble_att_init(void) { int rc; ble_att_preferred_mtu_val = MYNEWT_VAL(BLE_ATT_PREFERRED_MTU); rc = stats_init_and_reg( STATS_HDR(ble_att_stats), STATS_SIZE_INIT_PARMS(ble_att_stats, STATS_SIZE_32), STATS_NAME_INIT_PARMS(ble_att_stats), "ble_att"); if (rc != 0) { return BLE_HS_EOS; } return 0; }