• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  *****************************************************************************************
3  *
4  * @file bas.c
5  *
6  * @brief Battery Server Implementation.
7  *
8  *****************************************************************************************
9  * @attention
10   #####Copyright (c) 2019 GOODIX
11   All rights reserved.
12 
13     Redistribution and use in source and binary forms, with or without
14     modification, are permitted provided that the following conditions are met:
15   * Redistributions of source code must retain the above copyright
16     notice, this list of conditions and the following disclaimer.
17   * Redistributions in binary form must reproduce the above copyright
18     notice, this list of conditions and the following disclaimer in the
19     documentation and/or other materials provided with the distribution.
20   * Neither the name of GOODIX nor the names of its contributors may be used
21     to endorse or promote products derived from this software without
22     specific prior written permission.
23 
24   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27   ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE
28   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34   POSSIBILITY OF SUCH DAMAGE.
35  *****************************************************************************************
36  */
37 
38 /*
39  * INCLUDE FILES
40  *****************************************************************************************
41  */
42 #include "bas.h"
43 #include "ble_prf_types.h"
44 #include "ble_prf_utils.h"
45 #include "utility.h"
46 
47 /*
48  * ENUMERATIONS
49  *****************************************************************************************
50  */
51 /**@brief Battery Service Attributes Indexes. */
52 enum bas_attr_idx_t {
53     BAS_IDX_SVC,
54 
55     BAS_IDX_BATT_LVL_CHAR,
56     BAS_IDX_BATT_LVL_VAL,
57     BAS_IDX_BATT_LVL_NTF_CFG,
58     BAS_IDX_BATT_LVL_PRES_FMT,
59 
60     BAS_IDX_NB,
61 };
62 
63 /*
64  * STRUCTURES
65  *****************************************************************************************
66  */
67 /**@brief Battery Service environment variable. */
68 struct bas_env_t {
69     bas_init_t          bas_init;                       /**< Battery Service initialization variables. */
70     uint16_t            start_hdl;                      /**< Battery Service start handle. */
71     uint16_t
72     ntf_cfg[BAS_CONNECTION_MAX];    /**< The configuration of Battery Level Notification \
73                                          which is configured by the peer devices. */
74     prf_char_pres_fmt_t
75     batt_level_pres_format;         /**< Battery Level Characteristic Presentation Format \
76                                          which should not change during connection. */
77 };
78 
79 /*
80  * LOCAL FUNCTION DECLARATION
81  *****************************************************************************************
82  */
83 static sdk_err_t bas_init(void);
84 static void      bas_read_att_cb(uint8_t conn_idx, const gatts_read_req_cb_t *p_param);
85 static void      bas_write_att_cb(uint8_t conn_idx, const gatts_write_req_cb_t *p_param);
86 static void      bas_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value);
87 /*
88  * LOCAL VARIABLE DEFINITIONS
89  *****************************************************************************************
90  */
91 static struct bas_env_t s_bas_env[BAS_INSTANCE_MAX];  /**< Battery service instance. */
92 static uint8_t          s_bas_ins_cnt = 0;            /**< Number of Battery Server task instances. */
93 
94 /**@brief Full BAS Database Description which is used to add attributes into the ATT database. */
95 static const attm_desc_t bas_attr_tab[BAS_IDX_NB] = {
96     // Battery Service Declaration
97     [BAS_IDX_SVC] = {BLE_ATT_DECL_PRIMARY_SERVICE, READ_PERM_UNSEC, 0, 0},
98 
99     // Battery Level Characteristic - Declaration
100     [BAS_IDX_BATT_LVL_CHAR]     = {BLE_ATT_DECL_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
101     // Battery Level Characteristic - Value
102     [BAS_IDX_BATT_LVL_VAL]      = {
103         BLE_ATT_CHAR_BATTERY_LEVEL,
104         READ_PERM_UNSEC | NOTIFY_PERM_UNSEC,
105         ATT_VAL_LOC_USER,
106         BAS_LVL_MAX_LEN
107     },
108     // Battery Level Characteristic - Client Characteristic Configuration Descriptor
109     [BAS_IDX_BATT_LVL_NTF_CFG]  = {
110         BLE_ATT_DESC_CLIENT_CHAR_CFG,
111         READ_PERM_UNSEC | WRITE_REQ_PERM_UNSEC,
112         ATT_VAL_LOC_USER,
113         0
114     },
115     // Battery Level Characteristic - Characteristic Presentation Format Descriptor
116     [BAS_IDX_BATT_LVL_PRES_FMT] = {BLE_ATT_DESC_CHAR_PRES_FORMAT, READ_PERM_UNSEC, ATT_VAL_LOC_USER, 0},
117 };
118 
119 /**@brief Battery Service interface required by profile manager. */
120 static ble_prf_manager_cbs_t bas_mgr_cbs = {
121     (prf_init_func_t)bas_init,
122     NULL,
123     NULL
124 };
125 
126 /**@brief Battery GATT Server Callbacks. */
127 static gatts_prf_cbs_t bas_gatts_cbs = {
128     bas_read_att_cb,
129     bas_write_att_cb,
130     NULL,
131     NULL,
132     bas_cccd_set_cb
133 };
134 
135 /**@brief Battery Service Information. */
136 static const prf_server_info_t bas_prf_info = {
137     .max_connection_nb = BAS_CONNECTION_MAX,
138     .manager_cbs       = &bas_mgr_cbs,
139     .gatts_prf_cbs          = &bas_gatts_cbs
140 };
141 
142 /*
143  * LOCAL FUNCTION DEFINITIONS
144  *****************************************************************************************
145  */
146 /**
147  *****************************************************************************************
148  * @brief Initialize Battery service and create DB in ATT.
149  *
150  * @return Error code to know if service initialization succeed or not.
151  *****************************************************************************************
152  */
bas_init(void)153 static sdk_err_t bas_init(void)
154 {
155     const uint8_t     bas_svc_uuid[] = BLE_ATT_16_TO_16_ARRAY(BLE_ATT_SVC_BATTERY_SERVICE);
156     sdk_err_t         error_code     = SDK_SUCCESS;
157     uint16_t          start_hdl;
158     gatts_create_db_t gatts_db;
159 
160     error_code = memset_s(&gatts_db, sizeof(gatts_db), 0, sizeof(gatts_db));
161     if (error_code < 0) {
162         return error_code;
163     }
164 
165     for (uint8_t i = 0; i < s_bas_ins_cnt; i++) {
166         // The start hanlde must be set with PRF_INVALID_HANDLE to be allocated automatically by BLE Stack.
167         start_hdl                     = PRF_INVALID_HANDLE;
168         gatts_db.shdl                 = &start_hdl;
169         gatts_db.uuid                 = bas_svc_uuid;
170         gatts_db.attr_tab_cfg         = (uint8_t *)&s_bas_env[i].bas_init.char_mask;
171         gatts_db.max_nb_attr          = BAS_IDX_NB;
172         gatts_db.srvc_perm            = 0;
173         gatts_db.attr_tab_type        = SERVICE_TABLE_TYPE_16;
174         gatts_db.attr_tab.attr_tab_16 = bas_attr_tab;
175 
176         error_code = ble_gatts_srvc_db_create(&gatts_db);
177         if (SDK_SUCCESS == error_code) {
178             s_bas_env[i].start_hdl = *(gatts_db.shdl);
179         } else {
180             return error_code;
181         }
182     }
183 
184     return error_code;
185 }
186 
187 /**
188  *****************************************************************************************
189  * @brief Handles reception of the attribute info request message.
190  *
191  * @param[in] conn_idx: Connection index.
192  * @param[in] p_param:  Point to the parameters of the read request.
193  *****************************************************************************************
194  */
bas_read_att_cb(uint8_t conn_idx,const gatts_read_req_cb_t * p_param)195 static void bas_read_att_cb(uint8_t conn_idx, const gatts_read_req_cb_t *p_param)
196 {
197     uint8_t          handle = p_param->handle;
198     uint8_t          tab_index;
199     uint8_t          char_pres_value[PRF_CHAR_PRES_FMT_SIZE];
200     uint8_t          i;
201     gatts_read_cfm_t cfm;
202 
203     cfm.handle = handle;
204     cfm.status = BLE_SUCCESS;
205 
206     for (i = 0; i < s_bas_ins_cnt; i++) {
207         tab_index = prf_find_idx_by_handle(handle,
208                                            s_bas_env[i].start_hdl,
209                                            BAS_IDX_NB,
210                                            &s_bas_env[i].bas_init.char_mask);
211         if (tab_index > 0) {
212             break;
213         }
214     }
215 
216     switch (tab_index) {
217         case BAS_IDX_BATT_LVL_VAL:
218             cfm.length = sizeof(uint8_t);
219             cfm.value  = (uint8_t *)(&s_bas_env[i].bas_init.batt_lvl);
220             break;
221 
222         case BAS_IDX_BATT_LVL_NTF_CFG:
223             cfm.length = sizeof(uint16_t);
224             cfm.value  = (uint8_t *)(&(s_bas_env[i].ntf_cfg[conn_idx]));
225             break;
226 
227         case BAS_IDX_BATT_LVL_PRES_FMT:
228             cfm.length = PRF_CHAR_PRES_FMT_SIZE;
229             prf_pack_char_pres_fmt(char_pres_value, &(s_bas_env[i].batt_level_pres_format));
230             cfm.value = char_pres_value;
231             break;
232 
233         default:
234             cfm.length = 0;
235             cfm.status = BLE_ATT_ERR_INVALID_HANDLE;
236             break;
237     }
238 
239     ble_gatts_read_cfm(conn_idx, &cfm);
240 }
241 
242 /**
243  *****************************************************************************************
244  * @brief Handles reception of the write request.
245  *
246  * @param[in] conn_idx  Connection index.
247  * @param[in] p_param Point to the parameters of the write request.
248  *****************************************************************************************
249  */
bas_write_att_cb(uint8_t conn_idx,const gatts_write_req_cb_t * p_param)250 static void bas_write_att_cb(uint8_t conn_idx, const gatts_write_req_cb_t *p_param)
251 {
252     uint16_t          handle     = p_param->handle;
253     uint16_t          cccd_value = 0;
254     uint8_t           tab_index  = 0;
255     uint8_t           i          = 0;
256     bas_evt_t         bas_evt;
257     gatts_write_cfm_t cfm;
258 
259     cfm.handle = handle;
260     cfm.status = BLE_SUCCESS;
261 
262     for (i = 0; i < s_bas_ins_cnt; i++) {
263         tab_index = prf_find_idx_by_handle(handle,
264                                            s_bas_env[i].start_hdl,
265                                            BAS_IDX_NB,
266                                            &s_bas_env[i].bas_init.char_mask);
267         if (tab_index > 0) {
268             break;
269         }
270     }
271 
272     switch (tab_index) {
273         case BAS_IDX_BATT_LVL_NTF_CFG:
274             cccd_value = le16toh(&p_param->value[0]);
275             s_bas_env[i].ntf_cfg[conn_idx] = cccd_value;
276             bas_evt.evt_type = ((PRF_CLI_START_NTF == cccd_value) ? \
277                                 BAS_EVT_NOTIFICATION_ENABLED : \
278                                 BAS_EVT_NOTIFICATION_DISABLED);
279             break;
280 
281         default:
282             cfm.status = BLE_ATT_ERR_INVALID_HANDLE;
283             break;
284     }
285 
286     if (BLE_ATT_ERR_INVALID_HANDLE != cfm.status && \
287             BAS_EVT_INVALID != bas_evt.evt_type && \
288             s_bas_env[i].bas_init.evt_handler) {
289         bas_evt.conn_idx = conn_idx;
290         s_bas_env[i].bas_init.evt_handler(&bas_evt);
291     }
292 
293     ble_gatts_write_cfm(conn_idx, &cfm);
294 }
295 
296 /**
297  *****************************************************************************************
298  * @brief Handles reception of the cccd recover request.
299  *
300  * @param[in]: conn_idx:   Connection index
301  * @param[in]: handle:     The handle of cccd attribute.
302  * @param[in]: cccd_value: The value of cccd attribute.
303  *****************************************************************************************
304  */
bas_cccd_set_cb(uint8_t conn_idx,uint16_t handle,uint16_t cccd_value)305 static void bas_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value)
306 {
307     uint8_t           tab_index  = 0;
308     uint8_t           i          = 0;
309     bas_evt_t         bas_evt;
310 
311     if (!prf_is_cccd_value_valid(cccd_value)) {
312         return;
313     }
314 
315     for (i = 0; i < s_bas_ins_cnt; i++) {
316         tab_index = prf_find_idx_by_handle(handle,
317                                            s_bas_env[i].start_hdl,
318                                            BAS_IDX_NB,
319                                            &s_bas_env[i].bas_init.char_mask);
320         if (tab_index > 0) {
321             break;
322         }
323     }
324 
325     switch (tab_index) {
326         case BAS_IDX_BATT_LVL_NTF_CFG:
327             s_bas_env[i].ntf_cfg[conn_idx] = cccd_value;
328             bas_evt.evt_type = ((PRF_CLI_START_NTF == cccd_value) ? \
329                                 BAS_EVT_NOTIFICATION_ENABLED : \
330                                 BAS_EVT_NOTIFICATION_DISABLED);
331             break;
332 
333         default:
334             bas_evt.evt_type = BAS_EVT_INVALID;
335             break;
336     }
337 
338     if (BAS_EVT_INVALID != bas_evt.evt_type && s_bas_env[i].bas_init.evt_handler) {
339         bas_evt.conn_idx = conn_idx;
340         s_bas_env[i].bas_init.evt_handler(&bas_evt);
341     }
342 }
343 
344 /*
345  * GLOBAL FUNCTION DEFINITIONS
346  *******************************************************************************
347  */
bas_batt_lvl_update(uint8_t conn_idx,uint8_t ins_idx,uint8_t batt_lvl)348 sdk_err_t bas_batt_lvl_update(uint8_t conn_idx, uint8_t ins_idx, uint8_t batt_lvl)
349 {
350     sdk_err_t        error_code = SDK_ERR_NTF_DISABLED;
351     gatts_noti_ind_t send_cmd;
352 
353     if (ins_idx <= s_bas_ins_cnt) {
354         s_bas_env[ins_idx].bas_init.batt_lvl = batt_lvl;
355 
356         if (PRF_CLI_START_NTF == s_bas_env[ins_idx].ntf_cfg[conn_idx]) {
357             // Fill in the parameter structure
358             send_cmd.type   = BLE_GATT_NOTIFICATION;
359             send_cmd.handle = prf_find_handle_by_idx(BAS_IDX_BATT_LVL_VAL,
360                                                      s_bas_env[ins_idx].start_hdl,
361                                                      &s_bas_env[ins_idx].bas_init.char_mask);
362             // pack measured value in database
363             send_cmd.length = sizeof(uint8_t);
364             send_cmd.value  = &s_bas_env[ins_idx].bas_init.batt_lvl;
365 
366             // send notification to peer device
367             error_code = ble_gatts_noti_ind(conn_idx, &send_cmd);
368         }
369     }
370 
371     return error_code;
372 }
373 
bas_service_init(bas_init_t * p_bas_init,uint8_t ins_num)374 sdk_err_t bas_service_init(bas_init_t *p_bas_init, uint8_t ins_num)
375 {
376     sdk_err_t ret;
377     if (p_bas_init == NULL) {
378         return SDK_ERR_POINTER_NULL;
379     }
380 
381     if (ins_num > BAS_INSTANCE_MAX) {
382         return SDK_ERR_INVALID_PARAM;
383     }
384 
385     for (uint8_t i = 0; i < ins_num; i++) {
386         ret = memcpy_s(&s_bas_env[i].bas_init, sizeof(bas_init_t), &p_bas_init[i], sizeof(bas_init_t));
387         if (ret < 0) {
388             return ret;
389         }
390     }
391 
392     s_bas_ins_cnt = ins_num;
393 
394     return ble_server_prf_add(&bas_prf_info);
395 }
396 
397