1 /*****************************************************************************
2 * Nations Microcontroller Software Support
3 * ----------------------------------------------------------------------------
4 * Copyright (c) 2017, Nations Corporation
5 *
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * - Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the disclaimer below.
13 *
14 * Nations's name may not be used to endorse or promote products derived from
15 * this software without specific prior written permission.
16 *
17 * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY NATIONS "AS IS" AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
20 * DISCLAIMED. IN NO EVENT SHALL NATIONS BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
23 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 * ****************************************************************************/
28 /*****************************************************************************
29 * 文件名 :bass.c
30 * 功能描述:
31 * 版本:V 1.0.0
32 * 作者:
33 * 日期:
34 * ****************************************************************************/
35
36
37 /*
38 * INCLUDE FILES
39 ****************************************************************************************
40 */
41
42 #include "rwip_config.h"
43
44 #if (BLE_BATT_SERVER)
45 #include "bass.h"
46 #include "bass_task.h"
47 #include "prf_utils.h"
48 #include "prf.h"
49
50 #include "ke_mem.h"
51
52 /*
53 * BAS ATTRIBUTES DEFINITION
54 ****************************************************************************************
55 */
56
57 /// Full BAS Database Description - Used to add attributes into the database
58 const struct attm_desc bas_att_db[BAS_IDX_NB] =
59 {
60 // Battery Service Declaration
61 [BAS_IDX_SVC] = {ATT_DECL_PRIMARY_SERVICE, PERM(RD, ENABLE), 0, 0},
62 // Battery Level Characteristic Declaration
63 [BAS_IDX_BATT_LVL_CHAR] = {ATT_DECL_CHARACTERISTIC, PERM(RD, ENABLE), 0, 0},
64 // Battery Level Characteristic Value
65 [BAS_IDX_BATT_LVL_VAL] = {ATT_CHAR_BATTERY_LEVEL, PERM(RD, ENABLE), PERM(RI, ENABLE), 0},
66 // Battery Level Characteristic - Client Characteristic Configuration Descriptor
67 [BAS_IDX_BATT_LVL_NTF_CFG] = {ATT_DESC_CLIENT_CHAR_CFG, PERM(RD, ENABLE)|PERM(WRITE_REQ, ENABLE), 0, 0},
68 // Battery Level Characteristic - Characteristic Presentation Format Descriptor
69 [BAS_IDX_BATT_LVL_PRES_FMT] = {ATT_DESC_CHAR_PRES_FORMAT, PERM(RD, ENABLE), PERM(RI, ENABLE), 0},
70 };
71
72
73 /*
74 * LOCAL FUNCTION DEFINITIONS
75 ****************************************************************************************
76 */
77
78 /**
79 ****************************************************************************************
80 * @brief Initialization of the BASS module.
81 * This function performs all the initializations of the Profile module.
82 * - Creation of database (if it's a service)
83 * - Allocation of profile required memory
84 * - Initialization of task descriptor to register application
85 * - Task State array
86 * - Number of tasks
87 * - Default task handler
88 *
89 * @param[out] env Collector or Service allocated environment data.
90 * @param[in|out] start_hdl Service start handle (0 - dynamically allocated), only applies for services.
91 * @param[in] app_task Application task number.
92 * @param[in] sec_lvl Security level (AUTH, EKS and MI field of @see enum attm_value_perm_mask)
93 * @param[in] param Configuration parameters of profile collector or service (32 bits aligned)
94 *
95 * @return status code to know if profile initialization succeed or not.
96 ****************************************************************************************
97 */
bass_init(struct prf_task_env * env,uint16_t * start_hdl,uint16_t app_task,uint8_t sec_lvl,struct bass_db_cfg * params)98 static uint8_t bass_init (struct prf_task_env* env, uint16_t* start_hdl, uint16_t app_task, uint8_t sec_lvl, struct bass_db_cfg* params)
99 {
100
101 uint16_t shdl[BASS_NB_BAS_INSTANCES_MAX];
102 struct bass_env_tag* bass_env = NULL;
103 // Status
104 uint8_t status = GAP_ERR_NO_ERROR;
105 // Counter
106 uint8_t i;
107
108 // Check number of BAS instances
109 if ((params->bas_nb > 0) && (params->bas_nb <= BASS_NB_BAS_INSTANCES_MAX))
110 {
111 //-------------------- allocate memory required for the profile ---------------------
112 bass_env = (struct bass_env_tag* ) ke_malloc(sizeof(struct bass_env_tag), KE_MEM_ATT_DB);
113 memset(bass_env, 0 , sizeof(struct bass_env_tag));
114 // Save number of BAS
115 bass_env->svc_nb = params->bas_nb;
116 for (i = 0; ((i < params->bas_nb) && (status == GAP_ERR_NO_ERROR)); i++)
117 {
118 // Service content flag
119 uint8_t cfg_flag = BAS_CFG_FLAG_MANDATORY_MASK;
120 // Save database configuration
121 bass_env->features |= (params->features[i]) << i;
122 bass_env->batt_level_pres_format[i] = params->batt_level_pres_format[i];
123
124 // Check if notifications are supported
125 if (params->features[i] == BAS_BATT_LVL_NTF_SUP)
126 {
127 cfg_flag |= BAS_CFG_FLAG_NTF_SUP_MASK;
128 }
129
130 // Check if multiple instances
131 if (bass_env->svc_nb > 1)
132 {
133 cfg_flag |= BAS_CFG_FLAG_MTP_BAS_MASK;
134 }
135
136 shdl[i] = *start_hdl;
137
138 //Create BAS in the DB
139 //------------------ create the attribute database for the profile -------------------
140 status = attm_svc_create_db(&(shdl[i]), ATT_SVC_BATTERY_SERVICE, (uint8_t *)&cfg_flag,BAS_IDX_NB, NULL, env->task, bas_att_db,(sec_lvl & (PERM_MASK_SVC_DIS | PERM_MASK_SVC_AUTH | PERM_MASK_SVC_EKS)));
141 //Set optional permissions
142 if (status == GAP_ERR_NO_ERROR)
143 {
144 // update start handle for next service - only useful if multiple service, else not used.
145 // 4 characteristics + optional notification characteristic.
146 *start_hdl = shdl[i] + BAS_IDX_NB - ((params->features[i] == BAS_BATT_LVL_NTF_SUP) ? 0 : 1);
147 //Set optional permissions
148 if (params->features[i] == BAS_BATT_LVL_NTF_SUP)
149 {
150 // Battery Level characteristic value permissions
151 uint16_t perm = PERM(RD, ENABLE) | PERM(NTF, ENABLE);
152 attm_att_set_permission(shdl[i] + BAS_IDX_BATT_LVL_VAL, perm, 0);
153 }
154 }
155 // Reset configuration flag
156 cfg_flag = BAS_CFG_FLAG_MANDATORY_MASK;
157 }
158 }
159 else
160 {
161 status = PRF_ERR_INVALID_PARAM;
162 }
163
164 //-------------------- Update profile task information ---------------------
165 if (status == ATT_ERR_NO_ERROR)
166 {
167 // allocate BASS required environment variable
168 env->env = (prf_env_t*) bass_env;
169 *start_hdl = shdl[0];
170 bass_env->start_hdl = *start_hdl;
171 bass_env->prf_env.app_task = app_task | (PERM_GET(sec_lvl, SVC_MI) ? PERM(PRF_MI, ENABLE) : PERM(PRF_MI, DISABLE));
172 bass_env->prf_env.prf_task = env->task | PERM(PRF_MI, DISABLE);
173 // initialize environment variable
174 env->id = TASK_ID_BASS;
175 env->desc.idx_max = BASS_IDX_MAX;
176 env->desc.state = bass_env->state;
177 env->desc.default_handler = &bass_default_handler;
178 // service is ready, go into an Idle state
179 ke_state_set(env->task, BASS_IDLE);
180 }
181 else if (bass_env != NULL)
182 {
183 ke_free(bass_env);
184 }
185 return (status);
186 }
187 /**
188 ****************************************************************************************
189 * @brief Destruction of the BASS module - due to a reset for instance.
190 * This function clean-up allocated memory (attribute database is destroyed by another
191 * procedure)
192 *
193 * @param[in|out] env Collector or Service allocated environment data.
194 ****************************************************************************************
195 */
bass_destroy(struct prf_task_env * env)196 static void bass_destroy(struct prf_task_env* env)
197 {
198
199 struct bass_env_tag* bass_env = (struct bass_env_tag*) env->env;
200 // clear on-going operation
201 if (bass_env->operation != NULL)
202 {
203 ke_free(bass_env->operation);
204 }
205 // free profile environment variables
206 env->env = NULL;
207 ke_free(bass_env);
208 }
209
210 /**
211 ****************************************************************************************
212 * @brief Handles Connection creation
213 *
214 * @param[in|out] env Collector or Service allocated environment data.
215 * @param[in] conidx Connection index
216 ****************************************************************************************
217 */
bass_create(struct prf_task_env * env,uint8_t conidx)218 static void bass_create(struct prf_task_env* env, uint8_t conidx)
219 {
220
221 struct bass_env_tag* bass_env = (struct bass_env_tag*) env->env;
222 ASSERT_ERR(conidx < BLE_CONNECTION_MAX);
223 // force notification config to zero when peer device is connected
224 bass_env->ntf_cfg[conidx] = 0;
225 }
226
227 /**
228 ****************************************************************************************
229 * @brief Handles Disconnection
230 *
231 * @param[in|out] env Collector or Service allocated environment data.
232 * @param[in] conidx Connection index
233 * @param[in] reason Detach reason
234 ****************************************************************************************
235 */
bass_cleanup(struct prf_task_env * env,uint8_t conidx,uint8_t reason)236 static void bass_cleanup(struct prf_task_env* env, uint8_t conidx, uint8_t reason)
237 {
238
239 struct bass_env_tag* bass_env = (struct bass_env_tag*) env->env;
240 ASSERT_ERR(conidx < BLE_CONNECTION_MAX);
241 // force notification config to zero when peer device is disconnected
242 bass_env->ntf_cfg[conidx] = 0;
243 }
244
245
246 /**
247 ****************************************************************************************
248 * @brief Trigger battery level notification
249 *
250 * @param bass_env profile environment
251 * @param conidx peer destination connection index
252 * @param svc_idx Service index
253 ****************************************************************************************
254 */
bass_notify_batt_lvl(struct bass_env_tag * bass_env,uint8_t conidx,uint8_t svc_idx)255 static void bass_notify_batt_lvl(struct bass_env_tag* bass_env, uint8_t conidx, uint8_t svc_idx)
256 {
257
258 // Allocate the GATT notification message
259 struct gattc_send_evt_cmd *batt_lvl = KE_MSG_ALLOC_DYN(GATTC_SEND_EVT_CMD,KE_BUILD_ID(TASK_GATTC, conidx), prf_src_task_get(&(bass_env->prf_env),0),gattc_send_evt_cmd, sizeof(uint8_t));
260 // Fill in the parameter structure
261 batt_lvl->operation = GATTC_NOTIFY;
262 batt_lvl->handle = bass_get_att_handle(svc_idx, BAS_IDX_BATT_LVL_VAL);
263 // pack measured value in database
264 batt_lvl->length = sizeof(uint8_t);
265 batt_lvl->value[0] = bass_env->batt_lvl[svc_idx];
266 // send notification to peer device
267 ke_msg_send(batt_lvl);
268 }
269
270 /*
271 * GLOBAL VARIABLE DEFINITIONS
272 ****************************************************************************************
273 */
274
275 /// BASS Task interface required by profile manager
276 const struct prf_task_cbs bass_itf = {
277 (prf_init_fnct) bass_init,
278 bass_destroy,
279 bass_create,
280 bass_cleanup,
281 };
282
283 /*
284 * GLOBAL FUNCTIONS DEFINITIONS
285 ****************************************************************************************
286 */
287
bass_prf_itf_get(void)288 const struct prf_task_cbs* bass_prf_itf_get(void)
289 {
290 return &bass_itf;
291 }
292
bass_get_att_handle(uint8_t svc_idx,uint8_t att_idx)293 uint16_t bass_get_att_handle(uint8_t svc_idx, uint8_t att_idx)
294 {
295
296
297 struct bass_env_tag* bass_env = PRF_ENV_GET(BASS, bass);
298 uint16_t handle = ATT_INVALID_HDL;
299 uint8_t i = 0;
300 if (svc_idx < bass_env ->svc_nb)
301 {
302 handle = bass_env->start_hdl;
303 for(i = 0 ; i < svc_idx ; i++)
304 {
305 // update start handle for next service - only useful if multiple service, else not used.
306 // 4 characteristics + optional notification characteristic.
307 handle += BAS_IDX_NB - ((((bass_env->features >> i) & 0x01) == BAS_BATT_LVL_NTF_SUP) ? 0 : 1);
308 }
309 // increment index according to expected index
310 if (att_idx < BAS_IDX_BATT_LVL_NTF_CFG)
311 {
312 handle += att_idx;
313 }
314 // Battery notification
315 else if ((att_idx == BAS_IDX_BATT_LVL_NTF_CFG) && (((bass_env->features >> i) & 0x01) == BAS_BATT_LVL_NTF_SUP))
316 {
317 handle += BAS_IDX_BATT_LVL_NTF_CFG;
318 }
319 // Battery Level format
320 else if ((att_idx == BAS_IDX_BATT_LVL_PRES_FMT) && (bass_env->svc_nb > 1))
321 {
322 handle += BAS_IDX_BATT_LVL_PRES_FMT - ((((bass_env->features >> i) & 0x01) == BAS_BATT_LVL_NTF_SUP) ? 0 : 1);
323 }
324 else
325 {
326 handle = ATT_INVALID_HDL;
327 }
328 }
329 return handle;
330 }
331
bass_get_att_idx(uint16_t handle,uint8_t * svc_idx,uint8_t * att_idx)332 uint8_t bass_get_att_idx(uint16_t handle, uint8_t *svc_idx, uint8_t *att_idx)
333 {
334
335
336 struct bass_env_tag* bass_env = PRF_ENV_GET(BASS, bass);
337 uint16_t hdl_cursor = bass_env->start_hdl;
338 uint8_t status = PRF_APP_ERROR;
339
340 // Browse list of services
341 // handle must be greater than current index
342 for(*svc_idx = 0 ; (*svc_idx < bass_env->svc_nb) && (handle >= hdl_cursor) ; (*svc_idx)++)
343 {
344 // check if it's a mandatory index
345 if (handle <= (hdl_cursor + BAS_IDX_BATT_LVL_VAL))
346 {
347 *att_idx = handle -hdl_cursor;
348 status = GAP_ERR_NO_ERROR;
349 break;
350 }
351 hdl_cursor += BAS_IDX_BATT_LVL_VAL;
352
353 // check if it's a notify index
354 if (((bass_env->features >> *svc_idx) & 0x01) == BAS_BATT_LVL_NTF_SUP)
355 {
356 hdl_cursor++;
357 if (handle == hdl_cursor)
358 {
359 *att_idx = BAS_IDX_BATT_LVL_NTF_CFG;
360 status = GAP_ERR_NO_ERROR;
361 break;
362 }
363 }
364 // check if it's battery level format
365 if (bass_env->svc_nb > 1)
366 {
367 hdl_cursor++;
368 if (handle == hdl_cursor)
369 {
370 *att_idx = BAS_IDX_BATT_LVL_PRES_FMT;
371 status = GAP_ERR_NO_ERROR;
372 break;
373 }
374 }
375 hdl_cursor++;
376 }
377 return (status);
378 }
379
bass_exe_operation(void)380 void bass_exe_operation(void)
381 {
382
383
384 struct bass_env_tag* bass_env = PRF_ENV_GET(BASS, bass);
385 ASSERT_ERR(bass_env->operation != NULL);
386 bool finished = true;
387 uint8_t conidx = GAP_INVALID_CONIDX;
388
389 // Restoring connection information requested
390 if (bass_env->operation->id == BASS_ENABLE_REQ)
391 {
392 struct bass_enable_req * enable = (struct bass_enable_req *) ke_msg2param(bass_env->operation);
393 conidx = enable->conidx;
394 // loop on all services to check if notification should be triggered
395 while (bass_env->cursor < BASS_NB_BAS_INSTANCES_MAX)
396 {
397 if (((bass_env->ntf_cfg[enable->conidx] & (1 << bass_env->cursor)) != 0) && (enable->old_batt_lvl[bass_env->cursor] != bass_env->batt_lvl[bass_env->cursor]))
398 {
399 // trigger notification
400 bass_notify_batt_lvl(bass_env, enable->conidx, bass_env->cursor);
401 finished = false;
402 bass_env->cursor++;
403 break;
404 }
405 bass_env->cursor++;
406 }
407 }
408 // Battery level updated
409 else if (bass_env->operation->id == BASS_BATT_LEVEL_UPD_REQ)
410 {
411 struct bass_batt_level_upd_req * update = (struct bass_batt_level_upd_req *) ke_msg2param(bass_env->operation);
412 // loop on all connection
413 while (bass_env->cursor < BLE_CONNECTION_MAX)
414 {
415 if ((bass_env->ntf_cfg[bass_env->cursor] & (1 << update->bas_instance)) != 0)
416 {
417 // trigger notification
418 bass_notify_batt_lvl(bass_env, bass_env->cursor, update->bas_instance);
419 finished = false;
420 bass_env->cursor++;
421 break;
422 }
423 bass_env->cursor++;
424 }
425 }
426 else
427 {
428 ASSERT_ERR(0);
429 }
430
431 // check if operation is finished
432 if (finished)
433 {
434 // trigger response message
435 if (bass_env->operation->id == BASS_ENABLE_REQ)
436 {
437 struct bass_enable_rsp * rsp = KE_MSG_ALLOC(BASS_ENABLE_RSP, bass_env->operation->src_id,bass_env->operation->dest_id, bass_enable_rsp);
438 rsp->conidx = conidx;
439 rsp->status = GAP_ERR_NO_ERROR;
440 ke_msg_send(rsp);
441 }
442 else if (bass_env->operation->id == BASS_BATT_LEVEL_UPD_REQ)
443 {
444 struct bass_batt_level_upd_rsp * rsp = KE_MSG_ALLOC(BASS_BATT_LEVEL_UPD_RSP, bass_env->operation->src_id,bass_env->operation->dest_id, bass_batt_level_upd_rsp);
445 rsp->status = GAP_ERR_NO_ERROR;
446 ke_msg_send(rsp);
447 }
448 // free operation
449 ke_free(bass_env->operation);
450 bass_env->operation = NULL;
451 // go back to idle state
452 ke_state_set(prf_src_task_get(&(bass_env->prf_env), 0), BASS_IDLE);
453 }
454 }
455
456
457 #endif // (BLE_BATT_SERVER)
458
459 /// @} BASS
460
461
462