/****************************************************************************** * * Copyright (C) 2008-2012 Broadcom Corporation * * Licensed 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. * ******************************************************************************/ /****************************************************************************** * * this file contains ATT protocol functions * ******************************************************************************/ #include "bt_target.h" #if BLE_INCLUDED == TRUE #include "gatt_int.h" #include "l2c_api.h" #define GATT_HDR_FIND_TYPE_VALUE_LEN 21 #define GATT_OP_CODE_SIZE 1 /********************************************************************** ** ATT protocl message building utility * ***********************************************************************/ /******************************************************************************* ** ** Function attp_build_mtu_exec_cmd ** ** Description Build a exchange MTU request ** ** Returns None. ** *******************************************************************************/ BT_HDR *attp_build_mtu_cmd(UINT8 op_code, UINT16 rx_mtu) { BT_HDR *p_buf = NULL; UINT8 *p; if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + GATT_HDR_SIZE + L2CAP_MIN_OFFSET)) != NULL) { p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; UINT8_TO_STREAM (p, op_code); UINT16_TO_STREAM (p, rx_mtu); p_buf->offset = L2CAP_MIN_OFFSET; p_buf->len = GATT_HDR_SIZE; /* opcode + 2 bytes mtu */ } return p_buf; } /******************************************************************************* ** ** Function attp_build_exec_write_cmd ** ** Description Build a execute write request or response. ** ** Returns None. ** *******************************************************************************/ BT_HDR *attp_build_exec_write_cmd (UINT8 op_code, UINT8 flag) { BT_HDR *p_buf = NULL; UINT8 *p; if ((p_buf = (BT_HDR *)GKI_getbuf((UINT16)(sizeof(BT_HDR) + 10 + L2CAP_MIN_OFFSET))) != NULL) { p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; p_buf->offset = L2CAP_MIN_OFFSET; p_buf->len = GATT_OP_CODE_SIZE; UINT8_TO_STREAM (p, op_code); if (op_code == GATT_REQ_EXEC_WRITE) { flag &= GATT_PREP_WRITE_EXEC; UINT8_TO_STREAM (p, flag); p_buf->len += 1; } } return p_buf; } /******************************************************************************* ** ** Function attp_build_err_cmd ** ** Description Build a exchange MTU request ** ** Returns None. ** *******************************************************************************/ BT_HDR *attp_build_err_cmd(UINT8 cmd_code, UINT16 err_handle, UINT8 reason) { BT_HDR *p_buf = NULL; UINT8 *p; if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + L2CAP_MIN_OFFSET + 5)) != NULL) { p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; UINT8_TO_STREAM (p, GATT_RSP_ERROR); UINT8_TO_STREAM (p, cmd_code); UINT16_TO_STREAM(p, err_handle); UINT8_TO_STREAM (p, reason); p_buf->offset = L2CAP_MIN_OFFSET; /* GATT_HDR_SIZE (1B ERR_RSP op code+ 2B handle) + 1B cmd_op_code + 1B status */ p_buf->len = GATT_HDR_SIZE + 1 + 1; } return p_buf; } /******************************************************************************* ** ** Function attp_build_browse_cmd ** ** Description Build a read information request or read by type request ** ** Returns None. ** *******************************************************************************/ BT_HDR *attp_build_browse_cmd(UINT8 op_code, UINT16 s_hdl, UINT16 e_hdl, tBT_UUID uuid) { BT_HDR *p_buf = NULL; UINT8 *p; if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + 8 + L2CAP_MIN_OFFSET)) != NULL) { p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; /* Describe the built message location and size */ p_buf->offset = L2CAP_MIN_OFFSET; p_buf->len = GATT_OP_CODE_SIZE + 4; UINT8_TO_STREAM (p, op_code); UINT16_TO_STREAM (p, s_hdl); UINT16_TO_STREAM (p, e_hdl); p_buf->len += gatt_build_uuid_to_stream(&p, uuid); } return p_buf; } /******************************************************************************* ** ** Function attp_build_read_handles_cmd ** ** Description Build a read by type and value request. ** ** Returns pointer to the command buffer. ** *******************************************************************************/ BT_HDR *attp_build_read_handles_cmd (UINT16 payload_size, tGATT_FIND_TYPE_VALUE *p_value_type) { BT_HDR *p_buf = NULL; UINT8 *p; UINT16 len = p_value_type->value_len; if ((p_buf = (BT_HDR *)GKI_getbuf((UINT16)(sizeof(BT_HDR) + payload_size + L2CAP_MIN_OFFSET))) != NULL) { p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; p_buf->offset = L2CAP_MIN_OFFSET; p_buf->len = 5; /* opcode + s_handle + e_handle */ UINT8_TO_STREAM (p, GATT_REQ_FIND_TYPE_VALUE); UINT16_TO_STREAM (p, p_value_type->s_handle); UINT16_TO_STREAM (p, p_value_type->e_handle); p_buf->len += gatt_build_uuid_to_stream(&p, p_value_type->uuid); if (p_value_type->value_len + p_buf->len > payload_size ) len = payload_size - p_buf->len; memcpy (p, p_value_type->value, len); p_buf->len += len; } return p_buf; } /******************************************************************************* ** ** Function attp_build_read_multi_cmd ** ** Description Build a read multiple request ** ** Returns None. ** *******************************************************************************/ BT_HDR *attp_build_read_multi_cmd(UINT16 payload_size, UINT16 num_handle, UINT16 *p_handle) { BT_HDR *p_buf = NULL; UINT8 *p, i = 0; if ((p_buf = (BT_HDR *)GKI_getbuf((UINT16)(sizeof(BT_HDR) + num_handle * 2 + 1 + L2CAP_MIN_OFFSET))) != NULL) { p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; p_buf->offset = L2CAP_MIN_OFFSET; p_buf->len = 1; UINT8_TO_STREAM (p, GATT_REQ_READ_MULTI); for (i = 0; i < num_handle && p_buf->len + 2 <= payload_size; i ++) { UINT16_TO_STREAM (p, *(p_handle + i)); p_buf->len += 2; } } return p_buf; } /******************************************************************************* ** ** Function attp_build_handle_cmd ** ** Description Build a read /read blob request ** ** Returns None. ** *******************************************************************************/ BT_HDR *attp_build_handle_cmd(UINT8 op_code, UINT16 handle, UINT16 offset) { BT_HDR *p_buf = NULL; UINT8 *p; if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + 5 + L2CAP_MIN_OFFSET)) != NULL) { p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; p_buf->offset = L2CAP_MIN_OFFSET; UINT8_TO_STREAM (p, op_code); p_buf->len = 1; UINT16_TO_STREAM (p, handle); p_buf->len += 2; if (op_code == GATT_REQ_READ_BLOB) { UINT16_TO_STREAM (p, offset); p_buf->len += 2; } } return p_buf; } /******************************************************************************* ** ** Function attp_build_opcode_cmd ** ** Description Build a request/response with opcode only. ** ** Returns None. ** *******************************************************************************/ BT_HDR *attp_build_opcode_cmd(UINT8 op_code) { BT_HDR *p_buf = NULL; UINT8 *p; if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + 1 + L2CAP_MIN_OFFSET)) != NULL) { p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; p_buf->offset = L2CAP_MIN_OFFSET; UINT8_TO_STREAM (p, op_code); p_buf->len = 1; } return p_buf; } /******************************************************************************* ** ** Function attp_build_value_cmd ** ** Description Build a attribute value request ** ** Returns None. ** *******************************************************************************/ BT_HDR *attp_build_value_cmd (UINT16 payload_size, UINT8 op_code, UINT16 handle, UINT16 offset, UINT16 len, UINT8 *p_data) { BT_HDR *p_buf = NULL; UINT8 *p, *pp, pair_len, *p_pair_len; if ((p_buf = (BT_HDR *)GKI_getbuf((UINT16)(sizeof(BT_HDR) + payload_size + L2CAP_MIN_OFFSET))) != NULL) { p = pp =(UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; UINT8_TO_STREAM (p, op_code); p_buf->offset = L2CAP_MIN_OFFSET; p_buf->len = 1; if (op_code == GATT_RSP_READ_BY_TYPE) { p_pair_len = p; pair_len = len + 2; UINT8_TO_STREAM (p, pair_len); p_buf->len += 1; } if (op_code != GATT_RSP_READ_BLOB && op_code != GATT_RSP_READ) { UINT16_TO_STREAM (p, handle); p_buf->len += 2; } if (op_code == GATT_REQ_PREPARE_WRITE ||op_code == GATT_RSP_PREPARE_WRITE ) { UINT16_TO_STREAM (p, offset); p_buf->len += 2; } if (len > 0 && p_data != NULL) { /* ensure data not exceed MTU size */ if (payload_size - p_buf->len < len) { len = payload_size - p_buf->len; /* update handle value pair length */ if (op_code == GATT_RSP_READ_BY_TYPE) *p_pair_len = (len + 2); GATT_TRACE_WARNING1("attribute value too long, to be truncated to %d", len); } ARRAY_TO_STREAM (p, p_data, len); p_buf->len += len; } } return p_buf; } /******************************************************************************* ** ** Function attp_send_msg_to_L2CAP ** ** Description Send message to L2CAP. ** *******************************************************************************/ BOOLEAN attp_send_msg_to_L2CAP(tGATT_TCB *p_tcb, BT_HDR *p_toL2CAP) { UINT16 l2cap_ret; if (p_tcb->att_lcid == L2CAP_ATT_CID) l2cap_ret = L2CA_SendFixedChnlData (L2CAP_ATT_CID, p_tcb->peer_bda, p_toL2CAP); else l2cap_ret = (UINT16) L2CA_DataWrite (p_tcb->att_lcid, p_toL2CAP); if (l2cap_ret == L2CAP_DW_FAILED) { GATT_TRACE_ERROR1("ATT failed to pass msg:0x%0x to L2CAP", *((UINT8 *)(p_toL2CAP + 1) + p_toL2CAP->offset)); GKI_freebuf(p_toL2CAP); return FALSE; } else { return TRUE; } } /******************************************************************************* ** ** Function attp_build_sr_msg ** ** Description Build ATT Server PDUs. ** *******************************************************************************/ BT_HDR *attp_build_sr_msg(tGATT_TCB *p_tcb, UINT8 op_code, tGATT_SR_MSG *p_msg) { BT_HDR *p_cmd = NULL; UINT16 offset = 0; switch (op_code) { case GATT_RSP_READ_BLOB: GATT_TRACE_EVENT2 ("ATT_RSP_READ_BLOB: len = %d offset = %d", p_msg->attr_value.len, p_msg->attr_value.offset); offset = p_msg->attr_value.offset; case GATT_RSP_PREPARE_WRITE: if (offset == 0) offset = p_msg->attr_value.offset; case GATT_RSP_READ_BY_TYPE: case GATT_RSP_READ: case GATT_HANDLE_VALUE_NOTIF: case GATT_HANDLE_VALUE_IND: p_cmd = attp_build_value_cmd(p_tcb->payload_size, op_code, p_msg->attr_value.handle, offset, p_msg->attr_value.len, p_msg->attr_value.value); break; case GATT_RSP_WRITE: p_cmd = attp_build_opcode_cmd(op_code); break; case GATT_RSP_ERROR: p_cmd = attp_build_err_cmd(p_msg->error.cmd_code, p_msg->error.handle, p_msg->error.reason); break; case GATT_RSP_EXEC_WRITE: p_cmd = attp_build_exec_write_cmd(op_code, 0); break; case GATT_RSP_MTU: p_cmd = attp_build_mtu_cmd(op_code, p_msg->mtu); break; default: GATT_TRACE_DEBUG1("attp_build_sr_msg: unknown op code = %d", op_code); break; } if (!p_cmd) GATT_TRACE_ERROR0("No resources"); return p_cmd; } /******************************************************************************* ** ** Function attp_send_sr_msg ** ** Description This function sends the server response or indication message ** to client. ** ** Parameter p_tcb: pointer to the connecton control block. ** p_msg: pointer to message parameters structure. ** ** Returns GATT_SUCCESS if sucessfully sent; otherwise error code. ** ** *******************************************************************************/ tGATT_STATUS attp_send_sr_msg (tGATT_TCB *p_tcb, BT_HDR *p_msg) { tGATT_STATUS cmd_sent = GATT_NO_RESOURCES; if (p_tcb != NULL) { if (p_msg != NULL) { p_msg->offset = L2CAP_MIN_OFFSET; if (attp_send_msg_to_L2CAP (p_tcb, p_msg)) cmd_sent = GATT_SUCCESS; else cmd_sent = GATT_INTERNAL_ERROR; } } return cmd_sent; } /******************************************************************************* ** ** Function attp_cl_send_cmd ** ** Description Send a ATT command or enqueue it. ** ** Returns TRUE if command sent, otherwise FALSE. ** *******************************************************************************/ UINT8 attp_cl_send_cmd(tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 cmd_code, BT_HDR *p_cmd) { UINT8 att_ret = GATT_SUCCESS; if (p_tcb != NULL) { cmd_code &= ~GATT_AUTH_SIGN_MASK; if (p_tcb->pending_cl_req == p_tcb->next_slot_inq || cmd_code == GATT_HANDLE_VALUE_CONF) { /* no penindg request or value confirmation */ if (attp_send_msg_to_L2CAP(p_tcb, p_cmd)) { /* do not enq cmd if handle value confirmation or set request */ if (cmd_code != GATT_HANDLE_VALUE_CONF && cmd_code != GATT_CMD_WRITE) { gatt_start_rsp_timer (p_tcb); gatt_cmd_enq(p_tcb, clcb_idx, FALSE, cmd_code, NULL); } } else att_ret = GATT_INTERNAL_ERROR; } else gatt_cmd_enq(p_tcb, clcb_idx, TRUE, cmd_code, p_cmd); } else att_ret = GATT_ILLEGAL_PARAMETER; return att_ret; } /******************************************************************************* ** ** Function attp_send_cl_msg ** ** Description This function sends the client request or confirmation message ** to server. ** ** Parameter p_tcb: pointer to the connectino control block. ** clcb_idx: clcb index ** op_code: message op code. ** p_msg: pointer to message parameters structure. ** ** Returns GATT_SUCCESS if sucessfully sent; otherwise error code. ** ** *******************************************************************************/ tGATT_STATUS attp_send_cl_msg (tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code, tGATT_CL_MSG *p_msg) { tGATT_STATUS status = GATT_NO_RESOURCES; BT_HDR *p_cmd = NULL; UINT16 offset = 0, handle; if (p_tcb != NULL) { switch (op_code) { case GATT_REQ_MTU: if (p_msg->mtu <= GATT_MAX_MTU_SIZE) { p_tcb->payload_size = p_msg->mtu; p_cmd = attp_build_mtu_cmd(GATT_REQ_MTU, p_msg->mtu); } else status = GATT_ILLEGAL_PARAMETER; break; case GATT_REQ_FIND_INFO: case GATT_REQ_READ_BY_TYPE: case GATT_REQ_READ_BY_GRP_TYPE: if (GATT_HANDLE_IS_VALID (p_msg->browse.s_handle) && GATT_HANDLE_IS_VALID (p_msg->browse.e_handle) && p_msg->browse.s_handle <= p_msg->browse.e_handle) { p_cmd = attp_build_browse_cmd(op_code, p_msg->browse.s_handle, p_msg->browse.e_handle, p_msg->browse.uuid); } else status = GATT_ILLEGAL_PARAMETER; break; case GATT_REQ_READ_BLOB: offset = p_msg->read_blob.offset; /* fall through */ case GATT_REQ_READ: handle = (op_code == GATT_REQ_READ) ? p_msg->handle: p_msg->read_blob.handle; /* handle checking */ if (GATT_HANDLE_IS_VALID (handle)) { p_cmd = attp_build_handle_cmd(op_code, handle, offset); } else status = GATT_ILLEGAL_PARAMETER; break; case GATT_HANDLE_VALUE_CONF: p_cmd = attp_build_opcode_cmd(op_code); break; case GATT_REQ_PREPARE_WRITE: offset = p_msg->attr_value.offset; /* fall through */ case GATT_REQ_WRITE: case GATT_CMD_WRITE: case GATT_SIGN_CMD_WRITE: if (GATT_HANDLE_IS_VALID (p_msg->attr_value.handle)) { p_cmd = attp_build_value_cmd (p_tcb->payload_size, op_code, p_msg->attr_value.handle, offset, p_msg->attr_value.len, p_msg->attr_value.value); } else status = GATT_ILLEGAL_PARAMETER; break; case GATT_REQ_EXEC_WRITE: p_cmd = attp_build_exec_write_cmd(op_code, p_msg->exec_write); break; case GATT_REQ_FIND_TYPE_VALUE: p_cmd = attp_build_read_handles_cmd(p_tcb->payload_size, &p_msg->find_type_value); break; case GATT_REQ_READ_MULTI: p_cmd = attp_build_read_multi_cmd(p_tcb->payload_size, p_msg->read_multi.num_handles, p_msg->read_multi.handles); break; default: break; } if (p_cmd != NULL) status = attp_cl_send_cmd(p_tcb, clcb_idx, op_code, p_cmd); } else { GATT_TRACE_ERROR0("Peer device not connected"); } return status; } #endif