| /****************************************************************************** |
| * |
| * 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" |
| #include "btcore/include/uuid.h" |
| #include "osi/include/osi.h" |
| |
| #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. |
| *******************************************************************************/ |
| bool battery_valid_handle_range(uint16_t handle) |
| { |
| uint8_t 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_t battery_s_write_attr_value(uint8_t clcb_idx, tGATT_WRITE_REQ * p_value, |
| tGATT_STATUS *p_status) |
| { |
| uint8_t *p = p_value->value, i; |
| uint16_t handle = p_value->handle; |
| tBA_INST *p_inst = &battery_cb.battery_inst[0]; |
| tGATT_STATUS st = GATT_NOT_FOUND; |
| tBA_WRITE_DATA cfg; |
| uint8_t 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_t battery_s_read_attr_value (uint8_t clcb_idx, uint16_t handle, |
| UNUSED_ATTR tGATT_VALUE *p_value, bool is_long, |
| tGATT_STATUS* p_status) |
| { |
| uint8_t i; |
| tBA_INST *p_inst = &battery_cb.battery_inst[0]; |
| tGATT_STATUS st = GATT_NOT_FOUND; |
| uint8_t act = SRVC_ACT_RSP; |
| |
| 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 |
| ** |
| *******************************************************************************/ |
| bool battery_gatt_c_read_ba_req(UNUSED_ATTR uint16_t conn_id) |
| { |
| return true; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function battery_c_cmpl_cback |
| ** |
| ** Description Client operation complete callback. |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| void battery_c_cmpl_cback (UNUSED_ATTR tSRVC_CLCB *p_clcb, UNUSED_ATTR tGATTC_OPTYPE op, |
| UNUSED_ATTR tGATT_STATUS status, |
| UNUSED_ATTR tGATT_CL_COMPLETE *p_data) |
| { |
| } |
| |
| |
| /******************************************************************************* |
| ** |
| ** Function Battery_Instantiate |
| ** |
| ** Description Instantiate a Battery service |
| ** |
| *******************************************************************************/ |
| uint16_t Battery_Instantiate (uint8_t app_id, tBA_REG_INFO *p_reg_info) |
| { |
| uint16_t srvc_hdl = 0; |
| tGATT_STATUS status = GATT_ERROR; |
| tBA_INST *p_inst; |
| |
| 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]; |
| |
| btgatt_db_element_t service[BA_MAX_ATTR_NUM] = {}; |
| |
| bt_uuid_t service_uuid; |
| uuid_128_from_16(&service_uuid, UUID_SERVCLASS_BATTERY); |
| service[0].type = /* p_reg_info->is_pri */ BTGATT_DB_PRIMARY_SERVICE; |
| service[0].uuid = service_uuid; |
| |
| bt_uuid_t char_uuid; |
| uuid_128_from_16(&char_uuid, GATT_UUID_BATTERY_LEVEL); |
| service[1].type = BTGATT_DB_CHARACTERISTIC; |
| service[1].uuid = char_uuid; |
| service[1].properties = GATT_CHAR_PROP_BIT_READ; |
| if (p_reg_info->ba_level_descr & BA_LEVEL_NOTIFY) |
| service[1].properties |= GATT_CHAR_PROP_BIT_NOTIFY; |
| |
| int i=2; |
| if (p_reg_info->ba_level_descr & BA_LEVEL_NOTIFY) { |
| bt_uuid_t desc_uuid; |
| uuid_128_from_16(&desc_uuid, GATT_UUID_CHAR_CLIENT_CONFIG); |
| |
| service[i].type = BTGATT_DB_DESCRIPTOR; |
| service[i].uuid = desc_uuid; |
| service[i].permissions = (GATT_PERM_READ|GATT_PERM_WRITE); |
| i++; |
| } |
| |
| /* need presentation format descriptor? */ |
| if (p_reg_info->ba_level_descr & BA_LEVEL_PRE_FMT) { |
| bt_uuid_t desc_uuid; |
| uuid_128_from_16(&desc_uuid, GATT_UUID_CHAR_PRESENT_FORMAT); |
| |
| service[i].type = BTGATT_DB_DESCRIPTOR; |
| service[i].uuid = desc_uuid; |
| service[i].permissions = GATT_PERM_READ; |
| i++; |
| } |
| |
| /* need presentation format descriptor? */ |
| if (p_reg_info->ba_level_descr & BA_LEVEL_RPT_REF) { |
| bt_uuid_t desc_uuid; |
| uuid_128_from_16(&desc_uuid, GATT_UUID_RPT_REF_DESCR); |
| |
| service[i].type = BTGATT_DB_DESCRIPTOR; |
| service[i].uuid = desc_uuid; |
| service[i].permissions = GATT_PERM_READ; |
| i++; |
| } |
| |
| GATTS_AddService(srvc_eng_cb.gatt_if, service, i); |
| |
| if (status != GATT_SUCCESS) { |
| battery_cb.inst_id --; |
| GATT_TRACE_ERROR("%s: Failed to add battery servuce!", __func__); |
| } |
| |
| battery_cb.inst_id ++; |
| |
| p_inst->app_id = app_id; |
| p_inst->p_cback = p_reg_info->p_cback; |
| |
| srvc_hdl = service[0].attribute_handle; |
| p_inst->ba_level_hdl = service[1].attribute_handle; |
| |
| i=2; |
| if (p_reg_info->ba_level_descr & BA_LEVEL_NOTIFY) { |
| p_inst->clt_cfg_hdl = service[i].attribute_handle; |
| i++; |
| } |
| |
| if (p_reg_info->ba_level_descr & BA_LEVEL_PRE_FMT) { |
| p_inst->pres_fmt_hdl = service[i].attribute_handle; |
| i++; |
| } |
| |
| if (p_reg_info->ba_level_descr & BA_LEVEL_RPT_REF) { |
| p_inst->rpt_ref_hdl = service[i].attribute_handle; |
| i++; |
| } |
| |
| return srvc_hdl; |
| } |
| /******************************************************************************* |
| ** |
| ** Function Battery_Rsp |
| ** |
| ** Description Respond to a battery service request |
| ** |
| *******************************************************************************/ |
| void Battery_Rsp (uint8_t app_id, tGATT_STATUS st, uint8_t event, tBA_RSP_DATA *p_rsp) |
| { |
| tBA_INST *p_inst = &battery_cb.battery_inst[0]; |
| tGATTS_RSP rsp; |
| uint8_t *pp; |
| |
| uint8_t 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_t app_id, BD_ADDR remote_bda, uint8_t battery_level) |
| { |
| tBA_INST *p_inst = &battery_cb.battery_inst[0]; |
| uint8_t 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 |
| ** |
| *******************************************************************************/ |
| bool Battery_ReadBatteryLevel(UNUSED_ATTR BD_ADDR peer_bda) |
| { |
| /* to be implemented */ |
| return true; |
| } |