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