1 /******************************************************************************
2  *
3  *  Copyright (C) 1999-2013 Broadcom Corporation
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at:
8  *
9  *  http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  ******************************************************************************/
18 
19 #include "bt_target.h"
20 #include "bt_utils.h"
21 #include "gatt_api.h"
22 #include "gatt_int.h"
23 #include "srvc_eng_int.h"
24 #include "srvc_battery_int.h"
25 
26 #if BLE_INCLUDED == TRUE
27 
28 #define BA_MAX_CHAR_NUM          1
29 #define BA_MAX_ATTR_NUM          (BA_MAX_CHAR_NUM * 5 + 1) /* max 3 descriptors, 1 desclration and 1 value */
30 
31 #ifndef BATTER_LEVEL_PROP
32 #define BATTER_LEVEL_PROP           (GATT_CHAR_PROP_BIT_READ|GATT_CHAR_PROP_BIT_NOTIFY)
33 #endif
34 
35 
36 #ifndef BATTER_LEVEL_PERM
37 #define BATTER_LEVEL_PERM           (GATT_PERM_READ)
38 #endif
39 
40 tBATTERY_CB battery_cb;
41 
42 
43 /*******************************************************************************
44 **   battery_valid_handle_range
45 **
46 **   validate a handle to be a DIS attribute handle or not.
47 *******************************************************************************/
battery_valid_handle_range(UINT16 handle)48 BOOLEAN battery_valid_handle_range(UINT16 handle)
49 {
50     UINT8       i = 0;
51     tBA_INST    *p_inst = &battery_cb.battery_inst[0];
52 
53     for (;i < BA_MAX_INT_NUM; i ++, p_inst++)
54     {
55         if (handle == p_inst->ba_level_hdl ||
56             handle == p_inst->clt_cfg_hdl ||
57             handle == p_inst->rpt_ref_hdl ||
58             handle == p_inst->pres_fmt_hdl )
59         {
60             return TRUE;
61         }
62     }
63     return FALSE;
64 }
65 /*******************************************************************************
66 **   battery_s_write_attr_value
67 **
68 **   Process write DIS attribute request.
69 *******************************************************************************/
battery_s_write_attr_value(UINT8 clcb_idx,tGATT_WRITE_REQ * p_value,tGATT_STATUS * p_status)70 UINT8 battery_s_write_attr_value(UINT8 clcb_idx, tGATT_WRITE_REQ * p_value,
71                                  tGATT_STATUS *p_status)
72 {
73     UINT8       *p = p_value->value, i;
74     UINT16      handle = p_value->handle;
75     tBA_INST    *p_inst = &battery_cb.battery_inst[0];
76     tGATT_STATUS    st = GATT_NOT_FOUND;
77     tBA_WRITE_DATA   cfg;
78     UINT8       act = SRVC_ACT_RSP;
79 
80     for (i = 0; i < BA_MAX_INT_NUM; i ++, p_inst ++)
81     {
82         /* read battery level */
83         if (handle == p_inst->clt_cfg_hdl)
84         {
85             memcpy(cfg.remote_bda, srvc_eng_cb.clcb[clcb_idx].bda, BD_ADDR_LEN);
86             STREAM_TO_UINT16(cfg.clt_cfg, p);
87 
88             if (p_inst->p_cback)
89             {
90                 p_inst->pending_clcb_idx = clcb_idx;
91                 p_inst->pending_evt = BA_WRITE_CLT_CFG_REQ;
92                 p_inst->pending_handle = handle;
93                 cfg.need_rsp = p_value->need_rsp;
94                 act = SRVC_ACT_PENDING;
95 
96                 (* p_inst->p_cback)(p_inst->app_id, BA_WRITE_CLT_CFG_REQ, &cfg);
97             }
98         }
99         else /* all other handle is not writable */
100         {
101             st = GATT_WRITE_NOT_PERMIT;
102             break;
103         }
104     }
105     *p_status = st;
106 
107     return act;
108 }
109 /*******************************************************************************
110 **   BA Attributes Database Server Request callback
111 *******************************************************************************/
battery_s_read_attr_value(UINT8 clcb_idx,UINT16 handle,tGATT_VALUE * p_value,BOOLEAN is_long,tGATT_STATUS * p_status)112 UINT8 battery_s_read_attr_value (UINT8 clcb_idx, UINT16 handle, tGATT_VALUE *p_value, BOOLEAN is_long, tGATT_STATUS* p_status)
113 {
114     UINT8       i;
115     tBA_INST    *p_inst = &battery_cb.battery_inst[0];
116     tGATT_STATUS    st = GATT_NOT_FOUND;
117     UINT8       act = SRVC_ACT_RSP;
118     UNUSED(p_value);
119 
120     for (i = 0; i < BA_MAX_INT_NUM; i ++, p_inst ++)
121     {
122         /* read battery level */
123         if (handle == p_inst->ba_level_hdl ||
124             handle == p_inst->clt_cfg_hdl ||
125             handle == p_inst->rpt_ref_hdl ||
126             handle == p_inst->pres_fmt_hdl)
127             {
128             if (is_long)
129                 st = GATT_NOT_LONG;
130 
131                 if (p_inst->p_cback)
132                 {
133                 if (handle == p_inst->ba_level_hdl) p_inst->pending_evt = BA_READ_LEVEL_REQ;
134                 if (handle == p_inst->clt_cfg_hdl) p_inst->pending_evt = BA_READ_CLT_CFG_REQ;
135                 if (handle == p_inst->pres_fmt_hdl) p_inst->pending_evt = BA_READ_PRE_FMT_REQ;
136                 if (handle == p_inst->rpt_ref_hdl) p_inst->pending_evt = BA_READ_RPT_REF_REQ ;
137 
138                     p_inst->pending_clcb_idx = clcb_idx;
139                     p_inst->pending_handle = handle;
140                     act = SRVC_ACT_PENDING;
141 
142                 (* p_inst->p_cback)(p_inst->app_id, p_inst->pending_evt, NULL);
143                 }
144             else /* application is not registered */
145                 st = GATT_ERR_UNLIKELY;
146                 break;
147             }
148         /* else attribute not found */
149     }
150 
151 
152     *p_status = st;
153     return act;
154 }
155 
156 
157 /*******************************************************************************
158 **
159 ** Function         battery_gatt_c_read_ba_req
160 **
161 ** Description      Read remote device BA level attribute request.
162 **
163 ** Returns          void
164 **
165 *******************************************************************************/
battery_gatt_c_read_ba_req(UINT16 conn_id)166 BOOLEAN battery_gatt_c_read_ba_req(UINT16 conn_id)
167 {
168     UNUSED(conn_id);
169     return TRUE;
170 }
171 
172 /*******************************************************************************
173 **
174 ** Function         battery_c_cmpl_cback
175 **
176 ** Description      Client operation complete callback.
177 **
178 ** Returns          void
179 **
180 *******************************************************************************/
battery_c_cmpl_cback(tSRVC_CLCB * p_clcb,tGATTC_OPTYPE op,tGATT_STATUS status,tGATT_CL_COMPLETE * p_data)181 void battery_c_cmpl_cback (tSRVC_CLCB *p_clcb, tGATTC_OPTYPE op,
182                               tGATT_STATUS status, tGATT_CL_COMPLETE *p_data)
183 {
184     UNUSED(p_clcb);
185     UNUSED(op);
186     UNUSED(status);
187     UNUSED(p_data);
188 }
189 
190 
191 /*******************************************************************************
192 **
193 ** Function         Battery_Instantiate
194 **
195 ** Description      Instantiate a Battery service
196 **
197 *******************************************************************************/
Battery_Instantiate(UINT8 app_id,tBA_REG_INFO * p_reg_info)198 UINT16 Battery_Instantiate (UINT8 app_id, tBA_REG_INFO *p_reg_info)
199 {
200     tBT_UUID            uuid = {LEN_UUID_16, {UUID_SERVCLASS_BATTERY}};
201     UINT16              srvc_hdl;
202     tGATT_STATUS        status = GATT_ERROR;
203     tBA_INST            *p_inst;
204     tGATT_CHAR_PROP     prop = GATT_CHAR_PROP_BIT_READ;
205 
206     if (battery_cb.inst_id == BA_MAX_INT_NUM)
207     {
208         GATT_TRACE_ERROR("MAX battery service has been reached");
209         return 0;
210     }
211 
212     p_inst = &battery_cb.battery_inst[battery_cb.inst_id];
213 
214     srvc_hdl = GATTS_CreateService (srvc_eng_cb.gatt_if ,
215                                             &uuid,
216                                             battery_cb.inst_id ,
217                                             BA_MAX_ATTR_NUM,
218                                             p_reg_info->is_pri);
219 
220     if (srvc_hdl == 0)
221     {
222         GATT_TRACE_ERROR("Can not create service, Battery_Instantiate() failed!");
223         return 0;
224     }
225 
226     battery_cb.inst_id ++;
227 
228     p_inst->app_id  =   app_id;
229     p_inst->p_cback =   p_reg_info->p_cback;
230 
231     /* add battery level
232     */
233     uuid.uu.uuid16 = GATT_UUID_BATTERY_LEVEL;
234 
235     if (p_reg_info->ba_level_descr & BA_LEVEL_NOTIFY)
236         prop |= GATT_CHAR_PROP_BIT_NOTIFY;
237 
238     if ((p_inst->ba_level_hdl  = GATTS_AddCharacteristic(srvc_hdl,
239                                                 &uuid,
240                                                 BATTER_LEVEL_PERM,
241                                                 prop)) == 0)
242     {
243         GATT_TRACE_ERROR("Can not add Battery Level, Battery_Instantiate() failed!");
244         status = GATT_ERROR;
245     }
246     else
247     {
248         if (p_reg_info->ba_level_descr & BA_LEVEL_NOTIFY)
249         {
250             uuid.uu.uuid16 = GATT_UUID_CHAR_CLIENT_CONFIG;
251             p_inst->clt_cfg_hdl  = GATTS_AddCharDescriptor(srvc_hdl,
252                                                            (GATT_PERM_READ|GATT_PERM_WRITE),
253                                                            &uuid);
254             if (p_inst->clt_cfg_hdl == 0)
255             {
256                 GATT_TRACE_ERROR("Add battery level client notification FAILED!");
257             }
258         }
259         /* need presentation format descriptor? */
260         if (p_reg_info->ba_level_descr & BA_LEVEL_PRE_FMT)
261         {
262             uuid.uu.uuid16 = GATT_UUID_CHAR_PRESENT_FORMAT;
263             if ( (p_inst->pres_fmt_hdl = GATTS_AddCharDescriptor(srvc_hdl,
264                                                                  GATT_PERM_READ,
265                                                                  &uuid))
266                                        == 0)
267             {
268                 GATT_TRACE_ERROR("Add battery level presentation format descriptor FAILED!");
269             }
270 
271         }
272         /* need presentation format descriptor? */
273         if (p_reg_info->ba_level_descr & BA_LEVEL_RPT_REF)
274         {
275             uuid.uu.uuid16 = GATT_UUID_RPT_REF_DESCR;
276             if ( (p_inst->rpt_ref_hdl = GATTS_AddCharDescriptor(srvc_hdl,
277                                                                 GATT_PERM_READ,
278                                                                 &uuid))
279                                        == 0)
280             {
281                 GATT_TRACE_ERROR("Add battery level report reference descriptor FAILED!");
282             }
283 
284         }
285         /* start service
286         */
287         status = GATTS_StartService (srvc_eng_cb.gatt_if, srvc_hdl, p_reg_info->transport);
288     }
289 
290     if (status != GATT_SUCCESS)
291     {
292         battery_cb.inst_id --;
293         uuid.uu.uuid16 = UUID_SERVCLASS_BATTERY;
294         GATTS_DeleteService(srvc_eng_cb.gatt_if, &uuid, battery_cb.inst_id);
295         srvc_hdl = 0;
296     }
297 
298     return srvc_hdl;
299 }
300 /*******************************************************************************
301 **
302 ** Function         Battery_Rsp
303 **
304 ** Description      Respond to a battery service request
305 **
306 *******************************************************************************/
Battery_Rsp(UINT8 app_id,tGATT_STATUS st,UINT8 event,tBA_RSP_DATA * p_rsp)307 void Battery_Rsp (UINT8 app_id, tGATT_STATUS st, UINT8 event, tBA_RSP_DATA *p_rsp)
308 {
309     tBA_INST *p_inst = &battery_cb.battery_inst[0];
310     tGATTS_RSP  rsp;
311     UINT8   *pp;
312 
313     UINT8   i = 0;
314     while (i < BA_MAX_INT_NUM)
315     {
316         if (p_inst->app_id == app_id && p_inst->ba_level_hdl != 0)
317             break;
318         i ++;
319     }
320 
321     if (i == BA_MAX_INT_NUM)
322         return;
323 
324     memset(&rsp, 0, sizeof(tGATTS_RSP));
325 
326     if (p_inst->pending_evt == event)
327     {
328         switch (event)
329         {
330         case BA_READ_CLT_CFG_REQ:
331             rsp.attr_value.handle = p_inst->pending_handle;
332             rsp.attr_value.len = 2;
333             pp = rsp.attr_value.value;
334             UINT16_TO_STREAM(pp, p_rsp->clt_cfg);
335             srvc_sr_rsp(p_inst->pending_clcb_idx, st, &rsp);
336             break;
337 
338         case BA_READ_LEVEL_REQ:
339             rsp.attr_value.handle = p_inst->pending_handle;
340             rsp.attr_value.len = 1;
341             pp = rsp.attr_value.value;
342             UINT8_TO_STREAM(pp, p_rsp->ba_level);
343             srvc_sr_rsp(p_inst->pending_clcb_idx, st, &rsp);
344             break;
345 
346         case BA_WRITE_CLT_CFG_REQ:
347             srvc_sr_rsp(p_inst->pending_clcb_idx, st, NULL);
348             break;
349 
350         case BA_READ_RPT_REF_REQ:
351             rsp.attr_value.handle = p_inst->pending_handle;
352             rsp.attr_value.len = 2;
353             pp = rsp.attr_value.value;
354             UINT8_TO_STREAM(pp, p_rsp->rpt_ref.rpt_id);
355             UINT8_TO_STREAM(pp, p_rsp->rpt_ref.rpt_type);
356             srvc_sr_rsp(p_inst->pending_clcb_idx, st, &rsp);
357             break;
358 
359         default:
360             break;
361         }
362         p_inst->pending_clcb_idx = 0;
363         p_inst->pending_evt = 0;
364         p_inst->pending_handle = 0;
365     }
366     return;
367 }
368 /*******************************************************************************
369 **
370 ** Function         Battery_Notify
371 **
372 ** Description      Send battery level notification
373 **
374 *******************************************************************************/
Battery_Notify(UINT8 app_id,BD_ADDR remote_bda,UINT8 battery_level)375 void Battery_Notify (UINT8 app_id, BD_ADDR remote_bda, UINT8 battery_level)
376 {
377     tBA_INST *p_inst = &battery_cb.battery_inst[0];
378     UINT8    i = 0;
379 
380     while (i < BA_MAX_INT_NUM)
381     {
382         if (p_inst->app_id == app_id && p_inst->ba_level_hdl != 0)
383             break;
384         i ++;
385     }
386 
387     if (i == BA_MAX_INT_NUM || p_inst->clt_cfg_hdl == 0)
388         return;
389 
390     srvc_sr_notify(remote_bda, p_inst->ba_level_hdl, 1, &battery_level);
391 
392 }
393 /*******************************************************************************
394 **
395 ** Function         Battery_ReadBatteryLevel
396 **
397 ** Description      Read remote device Battery Level information.
398 **
399 ** Returns          void
400 **
401 *******************************************************************************/
Battery_ReadBatteryLevel(BD_ADDR peer_bda)402 BOOLEAN Battery_ReadBatteryLevel(BD_ADDR peer_bda)
403 {
404     UNUSED(peer_bda);
405     /* to be implemented */
406     return TRUE;
407 }
408 #endif  /* BLE_INCLUDED */
409