/****************************************************************************** * * Copyright (C) 1999-2013 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. * ******************************************************************************/ #include "bt_target.h" #include "bt_utils.h" #include "gatt_api.h" #include "gatt_int.h" #include "srvc_eng_int.h" #include "srvc_battery_int.h" #if BLE_INCLUDED == TRUE #define BA_MAX_CHAR_NUM 1 #define BA_MAX_ATTR_NUM (BA_MAX_CHAR_NUM * 5 + 1) /* max 3 descriptors, 1 desclration and 1 value */ #ifndef BATTER_LEVEL_PROP #define BATTER_LEVEL_PROP (GATT_CHAR_PROP_BIT_READ|GATT_CHAR_PROP_BIT_NOTIFY) #endif #ifndef BATTER_LEVEL_PERM #define BATTER_LEVEL_PERM (GATT_PERM_READ) #endif tBATTERY_CB battery_cb; /******************************************************************************* ** battery_valid_handle_range ** ** validate a handle to be a DIS attribute handle or not. *******************************************************************************/ BOOLEAN battery_valid_handle_range(UINT16 handle) { UINT8 i = 0; tBA_INST *p_inst = &battery_cb.battery_inst[0]; for (;i < BA_MAX_INT_NUM; i ++, p_inst++) { if (handle == p_inst->ba_level_hdl || handle == p_inst->clt_cfg_hdl || handle == p_inst->rpt_ref_hdl || handle == p_inst->pres_fmt_hdl ) { return TRUE; } } return FALSE; } /******************************************************************************* ** battery_s_write_attr_value ** ** Process write DIS attribute request. *******************************************************************************/ UINT8 battery_s_write_attr_value(UINT8 clcb_idx, tGATT_WRITE_REQ * p_value, tGATT_STATUS *p_status) { UINT8 *p = p_value->value, i; UINT16 handle = p_value->handle; tBA_INST *p_inst = &battery_cb.battery_inst[0]; tGATT_STATUS st = GATT_NOT_FOUND; tBA_WRITE_DATA cfg; UINT8 act = SRVC_ACT_RSP; for (i = 0; i < BA_MAX_INT_NUM; i ++, p_inst ++) { /* read battery level */ if (handle == p_inst->clt_cfg_hdl) { memcpy(cfg.remote_bda, srvc_eng_cb.clcb[clcb_idx].bda, BD_ADDR_LEN); STREAM_TO_UINT16(cfg.clt_cfg, p); if (p_inst->p_cback) { p_inst->pending_clcb_idx = clcb_idx; p_inst->pending_evt = BA_WRITE_CLT_CFG_REQ; p_inst->pending_handle = handle; cfg.need_rsp = p_value->need_rsp; act = SRVC_ACT_PENDING; (* p_inst->p_cback)(p_inst->app_id, BA_WRITE_CLT_CFG_REQ, &cfg); } } else /* all other handle is not writable */ { st = GATT_WRITE_NOT_PERMIT; break; } } *p_status = st; return act; } /******************************************************************************* ** BA Attributes Database Server Request callback *******************************************************************************/ UINT8 battery_s_read_attr_value (UINT8 clcb_idx, UINT16 handle, tGATT_VALUE *p_value, BOOLEAN is_long, tGATT_STATUS* p_status) { UINT8 i; tBA_INST *p_inst = &battery_cb.battery_inst[0]; tGATT_STATUS st = GATT_NOT_FOUND; UINT8 act = SRVC_ACT_RSP; UNUSED(p_value); for (i = 0; i < BA_MAX_INT_NUM; i ++, p_inst ++) { /* read battery level */ if (handle == p_inst->ba_level_hdl || handle == p_inst->clt_cfg_hdl || handle == p_inst->rpt_ref_hdl || handle == p_inst->pres_fmt_hdl) { if (is_long) st = GATT_NOT_LONG; if (p_inst->p_cback) { if (handle == p_inst->ba_level_hdl) p_inst->pending_evt = BA_READ_LEVEL_REQ; if (handle == p_inst->clt_cfg_hdl) p_inst->pending_evt = BA_READ_CLT_CFG_REQ; if (handle == p_inst->pres_fmt_hdl) p_inst->pending_evt = BA_READ_PRE_FMT_REQ; if (handle == p_inst->rpt_ref_hdl) p_inst->pending_evt = BA_READ_RPT_REF_REQ ; p_inst->pending_clcb_idx = clcb_idx; p_inst->pending_handle = handle; act = SRVC_ACT_PENDING; (* p_inst->p_cback)(p_inst->app_id, p_inst->pending_evt, NULL); } else /* application is not registered */ st = GATT_ERR_UNLIKELY; break; } /* else attribute not found */ } *p_status = st; return act; } /******************************************************************************* ** ** Function battery_gatt_c_read_ba_req ** ** Description Read remote device BA level attribute request. ** ** Returns void ** *******************************************************************************/ BOOLEAN battery_gatt_c_read_ba_req(UINT16 conn_id) { UNUSED(conn_id); return TRUE; } /******************************************************************************* ** ** Function battery_c_cmpl_cback ** ** Description Client operation complete callback. ** ** Returns void ** *******************************************************************************/ void battery_c_cmpl_cback (tSRVC_CLCB *p_clcb, tGATTC_OPTYPE op, tGATT_STATUS status, tGATT_CL_COMPLETE *p_data) { UNUSED(p_clcb); UNUSED(op); UNUSED(status); UNUSED(p_data); } /******************************************************************************* ** ** Function Battery_Instantiate ** ** Description Instantiate a Battery service ** *******************************************************************************/ UINT16 Battery_Instantiate (UINT8 app_id, tBA_REG_INFO *p_reg_info) { tBT_UUID uuid = {LEN_UUID_16, {UUID_SERVCLASS_BATTERY}}; UINT16 srvc_hdl; tGATT_STATUS status = GATT_ERROR; tBA_INST *p_inst; tGATT_CHAR_PROP prop = GATT_CHAR_PROP_BIT_READ; if (battery_cb.inst_id == BA_MAX_INT_NUM) { GATT_TRACE_ERROR("MAX battery service has been reached"); return 0; } p_inst = &battery_cb.battery_inst[battery_cb.inst_id]; srvc_hdl = GATTS_CreateService (srvc_eng_cb.gatt_if , &uuid, battery_cb.inst_id , BA_MAX_ATTR_NUM, p_reg_info->is_pri); if (srvc_hdl == 0) { GATT_TRACE_ERROR("Can not create service, Battery_Instantiate() failed!"); return 0; } battery_cb.inst_id ++; p_inst->app_id = app_id; p_inst->p_cback = p_reg_info->p_cback; /* add battery level */ uuid.uu.uuid16 = GATT_UUID_BATTERY_LEVEL; if (p_reg_info->ba_level_descr & BA_LEVEL_NOTIFY) prop |= GATT_CHAR_PROP_BIT_NOTIFY; if ((p_inst->ba_level_hdl = GATTS_AddCharacteristic(srvc_hdl, &uuid, BATTER_LEVEL_PERM, prop)) == 0) { GATT_TRACE_ERROR("Can not add Battery Level, Battery_Instantiate() failed!"); status = GATT_ERROR; } else { if (p_reg_info->ba_level_descr & BA_LEVEL_NOTIFY) { uuid.uu.uuid16 = GATT_UUID_CHAR_CLIENT_CONFIG; p_inst->clt_cfg_hdl = GATTS_AddCharDescriptor(srvc_hdl, (GATT_PERM_READ|GATT_PERM_WRITE), &uuid); if (p_inst->clt_cfg_hdl == 0) { GATT_TRACE_ERROR("Add battery level client notification FAILED!"); } } /* need presentation format descriptor? */ if (p_reg_info->ba_level_descr & BA_LEVEL_PRE_FMT) { uuid.uu.uuid16 = GATT_UUID_CHAR_PRESENT_FORMAT; if ( (p_inst->pres_fmt_hdl = GATTS_AddCharDescriptor(srvc_hdl, GATT_PERM_READ, &uuid)) == 0) { GATT_TRACE_ERROR("Add battery level presentation format descriptor FAILED!"); } } /* need presentation format descriptor? */ if (p_reg_info->ba_level_descr & BA_LEVEL_RPT_REF) { uuid.uu.uuid16 = GATT_UUID_RPT_REF_DESCR; if ( (p_inst->rpt_ref_hdl = GATTS_AddCharDescriptor(srvc_hdl, GATT_PERM_READ, &uuid)) == 0) { GATT_TRACE_ERROR("Add battery level report reference descriptor FAILED!"); } } /* start service */ status = GATTS_StartService (srvc_eng_cb.gatt_if, srvc_hdl, p_reg_info->transport); } if (status != GATT_SUCCESS) { battery_cb.inst_id --; uuid.uu.uuid16 = UUID_SERVCLASS_BATTERY; GATTS_DeleteService(srvc_eng_cb.gatt_if, &uuid, battery_cb.inst_id); srvc_hdl = 0; } return srvc_hdl; } /******************************************************************************* ** ** Function Battery_Rsp ** ** Description Respond to a battery service request ** *******************************************************************************/ void Battery_Rsp (UINT8 app_id, tGATT_STATUS st, UINT8 event, tBA_RSP_DATA *p_rsp) { tBA_INST *p_inst = &battery_cb.battery_inst[0]; tGATTS_RSP rsp; UINT8 *pp; UINT8 i = 0; while (i < BA_MAX_INT_NUM) { if (p_inst->app_id == app_id && p_inst->ba_level_hdl != 0) break; i ++; } if (i == BA_MAX_INT_NUM) return; memset(&rsp, 0, sizeof(tGATTS_RSP)); if (p_inst->pending_evt == event) { switch (event) { case BA_READ_CLT_CFG_REQ: rsp.attr_value.handle = p_inst->pending_handle; rsp.attr_value.len = 2; pp = rsp.attr_value.value; UINT16_TO_STREAM(pp, p_rsp->clt_cfg); srvc_sr_rsp(p_inst->pending_clcb_idx, st, &rsp); break; case BA_READ_LEVEL_REQ: rsp.attr_value.handle = p_inst->pending_handle; rsp.attr_value.len = 1; pp = rsp.attr_value.value; UINT8_TO_STREAM(pp, p_rsp->ba_level); srvc_sr_rsp(p_inst->pending_clcb_idx, st, &rsp); break; case BA_WRITE_CLT_CFG_REQ: srvc_sr_rsp(p_inst->pending_clcb_idx, st, NULL); break; case BA_READ_RPT_REF_REQ: rsp.attr_value.handle = p_inst->pending_handle; rsp.attr_value.len = 2; pp = rsp.attr_value.value; UINT8_TO_STREAM(pp, p_rsp->rpt_ref.rpt_id); UINT8_TO_STREAM(pp, p_rsp->rpt_ref.rpt_type); srvc_sr_rsp(p_inst->pending_clcb_idx, st, &rsp); break; default: break; } p_inst->pending_clcb_idx = 0; p_inst->pending_evt = 0; p_inst->pending_handle = 0; } return; } /******************************************************************************* ** ** Function Battery_Notify ** ** Description Send battery level notification ** *******************************************************************************/ void Battery_Notify (UINT8 app_id, BD_ADDR remote_bda, UINT8 battery_level) { tBA_INST *p_inst = &battery_cb.battery_inst[0]; UINT8 i = 0; while (i < BA_MAX_INT_NUM) { if (p_inst->app_id == app_id && p_inst->ba_level_hdl != 0) break; i ++; } if (i == BA_MAX_INT_NUM || p_inst->clt_cfg_hdl == 0) return; srvc_sr_notify(remote_bda, p_inst->ba_level_hdl, 1, &battery_level); } /******************************************************************************* ** ** Function Battery_ReadBatteryLevel ** ** Description Read remote device Battery Level information. ** ** Returns void ** *******************************************************************************/ BOOLEAN Battery_ReadBatteryLevel(BD_ADDR peer_bda) { UNUSED(peer_bda); /* to be implemented */ return TRUE; } #endif /* BLE_INCLUDED */