• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  *****************************************************************************************
3  *
4  * @file bcs.c
5  *
6  * @brief The implementation of Body Composition Service.
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 "bcs.h"
43 #include "wss_db.h"
44 #include "ble_prf_types.h"
45 #include "ble_prf_utils.h"
46 #include "utility.h"
47 #include "app_log.h"
48 #define PACKET_ADD_2 2
49 #define FIELD_SIZE 2
50 #define LOAD_LEN 2
51 
52 /*
53  * ENUMERATIONS
54  *****************************************************************************************
55  */
56 /** Body Composition Service Attributes Indexes. */
57 enum {
58     BCS_IDX_SVC,
59 
60     BCS_IDX_BC_FEAT_CHAR,
61     BCS_IDX_BC_FEAT_VAL,
62 
63     BCS_IDX_BC_MEAS_CHAR,
64     BCS_IDX_BC_MEAS_VAL,
65     BCS_IDX_BC_MEAS_IND_CFG,
66 
67     BCS_IDX_NB,
68 };
69 
70 /*
71  * STRUCTURES
72  *****************************************************************************************
73  */
74 /**@brief Body Composition Service Measurement packet. */
75 typedef struct {
76     uint16_t size;                          /**< Measurement packet size. */
77     uint8_t  value[BCS_MEAS_VAL_LEN_MAX];   /**< Measurement packet Value. */
78 } meas_packet_t;
79 
80 /**@brief Body Composition Service Measurement data stream. */
81 typedef struct {
82     meas_packet_t meas_packets[BCS_CACHE_MEAS_NUM_MAX * NUM_PACKETS];  /**< BCS Measurement packet Value. */
83     uint8_t       packet_num;                                          /**< BCS Measurement packet number. */
84     uint8_t       packet_to_be_send_num;   /**< Number of BCS Measurement packet to be send. */
85 } bcs_meas_data_stream_t;
86 
87 /**@brief Body Composition Service environment variable. */
88 struct bcs_env_t {
89     bcs_init_t        bcs_init;                            /**< Body Composition Service Init Value. */
90     uint16_t          start_hdl;                           /**< Body Composition Service start handle. */
91     uint16_t
92     meas_ind_cfg[BCS_CONNECTION_MAX];    /**< The configuration of BC Measurement Indication
93                                             which is configured by the peer devices. */
94     uint16_t          *p_start_handle;
95 };
96 
97 /*
98  * LOCAL FUNCTION DECLARATIONS
99  *****************************************************************************************
100  */
101 static sdk_err_t  bcs_init(void);
102 static void       bcs_write_att_cb(uint8_t conn_idx, const gatts_write_req_cb_t *p_param);
103 static void       bcs_read_att_cb(uint8_t conn_idx, const gatts_read_req_cb_t *p_param);
104 static void       bcs_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value);
105 static void       bcs_ntf_ind_cb(uint8_t conn_idx, uint8_t status, const ble_gatts_ntf_ind_t *p_ptf_ind);
106 static uint16_t   bcs_indicate_meas_value_chunk(uint8_t conn_idx);
107 static void       bcs_meas_value_encoded(const bcs_meas_val_t *p_meas, uint16_t max_payload, uint8_t cache_num);
108 static uint16_t   packet_field_add(uint16_t flag, uint16_t value, uint16_t *p_flags, uint8_t **pp_field);
109 static bool       subsequent_packet_switched(uint16_t max_payload, uint8_t field_size, meas_packet_t *p_packets);
110 
111 /*
112  * LOCAL VARIABLE DEFINITIONS
113  *****************************************************************************************
114  */
115 static struct bcs_env_t s_bcs_env;
116 static bcs_meas_data_stream_t s_meas_packets;
117 
118 /**@brief Full BCS attributes descriptor which is used to add attributes into the ATT database.*/
119 static const attm_desc_t bcs_attr_tab[BCS_IDX_NB] = {
120     // Body Composition Service Declaration
121     [BCS_IDX_SVC]             = {BLE_ATT_DECL_SECONDARY_SERVICE, READ_PERM_UNSEC, 0, 0},
122 
123     // BC Feature Characteristic - Declaration
124     [BCS_IDX_BC_FEAT_CHAR]    = {BLE_ATT_DECL_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
125     // BC Feature Characteristic - Value
126     [BCS_IDX_BC_FEAT_VAL]     = {
127         BLE_ATT_CHAR_BODY_COMPOSITION_FEATURE,
128         READ_PERM(UNAUTH),
129         ATT_VAL_LOC_USER,
130         BCS_FEAT_VAL_LEN_MAX
131     },
132 
133     // BC Measurement Characteristic - Declaration
134     [BCS_IDX_BC_MEAS_CHAR]    = {BLE_ATT_DECL_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
135     // BC Measurement Characteristic - Value
136     [BCS_IDX_BC_MEAS_VAL]     = {
137         BLE_ATT_CHAR_BODY_COMPOSITION_MEASUREMENT,
138         INDICATE_PERM(UNAUTH),
139         ATT_VAL_LOC_USER,
140         BCS_MEAS_VAL_LEN_MAX
141     },
142     // BC Measurement Characteristic - Client Characteristic Configuration Descriptor
143     [BCS_IDX_BC_MEAS_IND_CFG] = {
144         BLE_ATT_DESC_CLIENT_CHAR_CFG,
145         READ_PERM(UNAUTH) | WRITE_REQ_PERM(UNAUTH),
146         0,
147         0
148     },
149 };
150 
151 /**@brief BCS Callbacks required by profile manager. */
152 static ble_prf_manager_cbs_t bcs_mgr_cbs = {
153     (prf_init_func_t) bcs_init,
154     NULL,
155     NULL
156 };
157 
158 /**@brief BCS Callbacks for GATT server. */
159 static gatts_prf_cbs_t bcs_cb_func = {
160     bcs_read_att_cb,
161     bcs_write_att_cb,
162     NULL,
163     bcs_ntf_ind_cb,
164     bcs_cccd_set_cb,
165 };
166 
167 /**@brief Information for registering BC service. */
168 static const prf_server_info_t bcs_prf_info = {
169     .max_connection_nb = BCS_CONNECTION_MAX,
170     .manager_cbs       = &bcs_mgr_cbs,
171     .gatts_prf_cbs     = &bcs_cb_func
172 };
173 
174 /*
175  * LOCAL FUNCTION DEFINITIONS
176  *****************************************************************************************
177  */
178 /**
179  *****************************************************************************************
180  * @brief Initialize Body Composition service create db in att
181  *
182  * @return Error code to know if profile initialization succeed or not.
183  *****************************************************************************************
184  */
bcs_init(void)185 static sdk_err_t bcs_init(void)
186 {
187     *s_bcs_env.p_start_handle = PRF_INVALID_HANDLE;
188     const uint8_t     bcs_svc_uuid[] = BLE_ATT_16_TO_16_ARRAY(BLE_ATT_SVC_BODY_COMPOSITION);
189     sdk_err_t         error_code;
190     gatts_create_db_t gatts_db;
191 
192     error_code = memset_s(&gatts_db, sizeof(gatts_db), 0, sizeof(gatts_db));
193     if (error_code < 0) {
194         return error_code;
195     }
196 
197     gatts_db.shdl                 = s_bcs_env.p_start_handle;
198     gatts_db.uuid                 = bcs_svc_uuid;
199     gatts_db.attr_tab_cfg         = (uint8_t *)&(s_bcs_env.bcs_init.char_mask);
200     gatts_db.max_nb_attr          = BCS_IDX_NB;
201     gatts_db.srvc_perm            = SRVC_SECONDARY_SET;
202     gatts_db.attr_tab_type        = SERVICE_TABLE_TYPE_16;
203     gatts_db.attr_tab.attr_tab_16 = bcs_attr_tab;
204 
205     error_code = ble_gatts_srvc_db_create(&gatts_db);
206     if (SDK_SUCCESS == error_code) {
207         s_bcs_env.start_hdl = *gatts_db.shdl;
208     }
209 
210     return error_code;
211 }
212 
213 /**
214  *****************************************************************************************
215  * @brief Handles reception of the read request.
216  *
217  * @param[in] conn_idx: Connection index.
218  * @param[in] p_param:  Pointer to the parameters of the read request.
219  *
220  * @return If the request was consumed or not.
221  *****************************************************************************************
222  */
bcs_read_att_cb(uint8_t conn_idx,const gatts_read_req_cb_t * p_param)223 static void bcs_read_att_cb(uint8_t conn_idx, const gatts_read_req_cb_t *p_param)
224 {
225     gatts_read_cfm_t cfm;
226     uint8_t handle = p_param->handle;
227     uint8_t tab_index = prf_find_idx_by_handle(handle,
228                         s_bcs_env.start_hdl,
229                         BCS_IDX_NB,
230                         (uint8_t *)&(s_bcs_env.bcs_init.char_mask));
231 
232     cfm.handle = handle;
233     cfm.status = BLE_SUCCESS;
234     switch (tab_index) {
235         case BCS_IDX_BC_FEAT_VAL:
236             cfm.length = sizeof(uint32_t);
237             cfm.value = (uint8_t *)&s_bcs_env.bcs_init.feature;
238             break;
239 
240         case BCS_IDX_BC_MEAS_IND_CFG:
241             cfm.length = sizeof(uint16_t);
242             cfm.value = (uint8_t *)&s_bcs_env.meas_ind_cfg[conn_idx];
243             break;
244 
245         default:
246             cfm.length = 0;
247             cfm.status = BLE_ATT_ERR_INVALID_HANDLE;
248             break;
249     }
250 
251     ble_gatts_read_cfm(conn_idx, &cfm);
252 }
253 
254 /**
255  *****************************************************************************************
256  * @brief Handles reception of the write request.
257  *
258  * @param[in] conn_idx: Connection index.
259  * @param[in] p_param:  Pointer to the parameters of the write request.
260  *
261  * @return If the request was consumed or not.
262  *****************************************************************************************
263  */
bcs_write_att_cb(uint8_t conn_idx,const gatts_write_req_cb_t * p_param)264 static void bcs_write_att_cb(uint8_t conn_idx, const gatts_write_req_cb_t *p_param)
265 {
266     uint16_t          handle     = p_param->handle;
267     uint16_t          tab_index  = 0;
268     uint16_t          cccd_value = 0;
269     bcs_evt_t         event;
270     gatts_write_cfm_t cfm;
271 
272     tab_index = prf_find_idx_by_handle(handle,
273                                        s_bcs_env.start_hdl,
274                                        BCS_IDX_NB,
275                                        (uint8_t *)&s_bcs_env.bcs_init.char_mask);
276     cfm.handle     = handle;
277     cfm.status     = BLE_SUCCESS;
278     event.evt_type = BCS_EVT_INVALID;
279     event.conn_idx = conn_idx;
280 
281     switch (tab_index) {
282         case BCS_IDX_BC_MEAS_IND_CFG:
283             cccd_value = le16toh(&p_param->value[0]);
284             event.evt_type = ((PRF_CLI_START_IND == cccd_value) ?\
285                               BCS_EVT_MEAS_INDICATION_ENABLE :\
286                               BCS_EVT_MEAS_INDICATION_DISABLE);
287             s_bcs_env.meas_ind_cfg[conn_idx] = cccd_value;
288             break;
289 
290         default:
291             cfm.status = BLE_ATT_ERR_INVALID_HANDLE;
292             break;
293     }
294 
295     ble_gatts_write_cfm(conn_idx, &cfm);
296 
297     if (BLE_ATT_ERR_INVALID_HANDLE != cfm.status &&
298         BCS_EVT_INVALID != event.evt_type && s_bcs_env.bcs_init.evt_handler) {
299         s_bcs_env.bcs_init.evt_handler(&event);
300     }
301 }
302 
303 /**
304  *****************************************************************************************
305  * @brief Handles reception of the cccd recover request.
306  *
307  * @param[in]: conn_idx:   Connection index
308  * @param[in]: handle:     The handle of cccd attribute.
309  * @param[in]: cccd_value: The value of cccd attribute.
310  *****************************************************************************************
311  */
bcs_cccd_set_cb(uint8_t conn_idx,uint16_t handle,uint16_t cccd_value)312 static void bcs_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value)
313 {
314     uint16_t         tab_index  = 0;
315     bcs_evt_t        event;
316 
317     if (!prf_is_cccd_value_valid(cccd_value)) {
318         return;
319     }
320 
321     tab_index  = prf_find_idx_by_handle(handle,
322                                         s_bcs_env.start_hdl,
323                                         BCS_IDX_NB,
324                                         (uint8_t *)&s_bcs_env.bcs_init.char_mask);
325 
326     event.evt_type = BCS_EVT_INVALID;
327     event.conn_idx = conn_idx;
328 
329     switch (tab_index) {
330         case BCS_IDX_BC_MEAS_IND_CFG:
331             event.evt_type = ((PRF_CLI_START_IND == cccd_value) ?\
332                               BCS_EVT_MEAS_INDICATION_ENABLE :\
333                               BCS_EVT_MEAS_INDICATION_DISABLE);
334             s_bcs_env.meas_ind_cfg[conn_idx] = cccd_value;
335             break;
336 
337         default:
338             break;
339     }
340 
341     if (BCS_EVT_INVALID != event.evt_type && s_bcs_env.bcs_init.evt_handler) {
342         s_bcs_env.bcs_init.evt_handler(&event);
343     }
344 }
345 
346 /**
347  *****************************************************************************************
348  * @brief Handles reception of the complete event.
349  *
350  * @param[in] conn_idx:   Connection index
351  * @param[in] status:     Complete event status.
352  * @param[in] p_ntf_ind:  Pointer to the parameters of the complete event.
353  *****************************************************************************************
354  */
bcs_ntf_ind_cb(uint8_t conn_idx,uint8_t status,const ble_gatts_ntf_ind_t * p_ntf_ind)355 static void bcs_ntf_ind_cb(uint8_t conn_idx, uint8_t status, const ble_gatts_ntf_ind_t *p_ntf_ind)
356 {
357     bcs_evt_t  event;
358 
359     event.evt_type = BCS_EVT_INVALID;
360     event.conn_idx = conn_idx;
361 
362     if (s_bcs_env.bcs_init.evt_handler && SDK_SUCCESS == status) {
363         if (BLE_GATT_INDICATION == p_ntf_ind->type) {
364             bcs_indicate_meas_value_chunk(conn_idx);
365             event.evt_type = BCS_EVT_MEAS_INDICATION_CPLT;
366         }
367         s_bcs_env.bcs_init.evt_handler(&event);
368     }
369 }
370 
371 /**
372  *****************************************************************************************
373  * @brief Initialize the packet with mandatory data.
374  *
375  * @param[in]  p_meas:   Pointer to the measurement data.
376  * @param[in]  p_pkt:    Pointer to the encoded packet.
377  * @param[in]  pp_flags: Point to pointer of flags in encoded packet.
378  * @param[in]  pp_field: Point to pointer of field in encoded packet.
379  *
380  * @return The size of mandatory data in encoded packet.
381  *****************************************************************************************
382  */
meas_packet_init(const bcs_meas_val_t * p_meas,meas_packet_t * p_pkt,uint16_t ** pp_flags,uint8_t ** pp_field)383 static uint16_t meas_packet_init(const bcs_meas_val_t *p_meas, meas_packet_t *p_pkt,
384                                  uint16_t **pp_flags, uint8_t **pp_field)
385 {
386     *pp_flags = (uint16_t *)p_pkt->value;
387     /* the first 2 octets are reserved for flags. */
388     *pp_field = &p_pkt->value[2];
389 
390     uint16_t *p_flags = *pp_flags;
391     *p_flags |= (s_bcs_env.bcs_init.bcs_unit == BCS_UNIT_SI) ?
392                 BCS_MEAS_FLAG_UNIT_SI : BCS_MEAS_FLAG_UNIT_IMPERIAL;
393 
394     uint16_t *p_field = (uint16_t *)*pp_field;
395     *p_field = p_meas->body_fat_percentage;
396 
397     *pp_field += sizeof(p_meas->body_fat_percentage);
398 
399     return (sizeof(*p_flags) + sizeof(p_meas->body_fat_percentage));
400 }
401 
402 /**
403  *****************************************************************************************
404  * @brief Add a field into a packet.
405  *
406  * @param[in]  flag:     The flag of value to be added.
407  * @param[in]  value:    16bits value to be added.
408  * @param[in]  p_flags:  Pointer to the flags in encoded packet.
409  * @param[in]  pp_field: Point to pointer of the filed in encoded packet.
410  *
411  * @return The size of value being added.
412  *****************************************************************************************
413  */
packet_field_add(uint16_t flag,uint16_t value,uint16_t * p_flags,uint8_t ** pp_field)414 static uint16_t packet_field_add(uint16_t flag, uint16_t value,
415                                  uint16_t *p_flags, uint8_t **pp_field)
416 {
417     *p_flags |= flag;
418 
419     uint16_t *p_field = (uint16_t *)*pp_field;
420     *p_field = value;
421 
422     *pp_field += sizeof(value);
423 
424     return sizeof(value);
425 }
426 
427 /**
428  *****************************************************************************************
429  * @brief Check if switch to subsequent packet.
430  *
431  * @param[in] max_payload: The max size of payload which is carried on ATT,
432  *                         equals (MTU - MEASUREMENT_VAL_MAX_LEN).
433  * @param[in] field_size:  The size of field to be added.
434  * @param[in] p_packets:   Pointer to array of the packets where the encoded
435  *                         data will be written.
436  *
437  * @return true for switching to subsequent packet.
438  *****************************************************************************************
439  */
subsequent_packet_switched(uint16_t max_payload,uint8_t field_size,meas_packet_t * p_packets)440 static bool subsequent_packet_switched(uint16_t max_payload, uint8_t field_size,
441                                        meas_packet_t *p_packets)
442 {
443     if (p_packets[MEAS_PACKET_SUB].size) {
444         return false;
445     }
446 
447     meas_packet_t *p_first_pkt = &p_packets[MEAS_PACKET_FIRST];
448 
449     if (p_first_pkt->size + field_size > max_payload) {
450         // The Multiple packet bit is setting for the first packet.
451         uint16_t *p_flags = (uint16_t *)p_first_pkt->value;
452         *p_flags |= BCS_MEAS_FLAG_MUTI_PACKET;
453 
454         meas_packet_t *p_sub_pkt = &p_packets[MEAS_PACKET_SUB];
455 
456         // The Multiple packet bit is setting for the second packet.
457         p_flags = (uint16_t *)p_sub_pkt->value;
458         *p_flags |= BCS_MEAS_FLAG_MUTI_PACKET;
459 
460         return true;
461     } else {
462         return false;
463     }
464 }
465 
466 /**
467  *****************************************************************************************
468  * @brief Handle Body Composition Measurement data indication.
469  *
470  * @param[in] conn_idx: Connection index.
471  *
472  * @return Result of handle.
473  *****************************************************************************************
474  */
bcs_indicate_meas_value_chunk(uint8_t conn_idx)475 static uint16_t bcs_indicate_meas_value_chunk(uint8_t conn_idx)
476 {
477     gatts_noti_ind_t bcs_meas_ind;
478     sdk_err_t        error_code;
479 
480     if (s_meas_packets.packet_to_be_send_num == 0) {
481         s_meas_packets.packet_num            = 0;
482         s_meas_packets.packet_to_be_send_num = 0;
483         error_code = memset_s(s_meas_packets.meas_packets, sizeof(s_meas_packets.meas_packets),
484                               0x00, sizeof(s_meas_packets.meas_packets));
485         if (error_code < 0) {
486             return error_code;
487         }
488 
489         return SDK_SUCCESS;
490     }
491 
492     if (PRF_CLI_START_IND == s_bcs_env.meas_ind_cfg[conn_idx]) {
493         bcs_meas_ind.type   = BLE_GATT_INDICATION;
494         bcs_meas_ind.handle = prf_find_handle_by_idx(BCS_IDX_BC_MEAS_VAL,
495             s_bcs_env.start_hdl,
496             (uint8_t *)&s_bcs_env.bcs_init.char_mask);
497         bcs_meas_ind.length = s_meas_packets.meas_packets[s_meas_packets.packet_num -
498                                                         s_meas_packets.packet_to_be_send_num].size;
499         bcs_meas_ind.value  = s_meas_packets.meas_packets[s_meas_packets.packet_num -
500                                                         s_meas_packets.packet_to_be_send_num].value;
501 
502         error_code = ble_gatts_noti_ind(conn_idx, &bcs_meas_ind);
503     }
504 
505     if (SDK_SUCCESS == error_code) {
506         s_meas_packets.packet_to_be_send_num--;
507     }
508     return error_code;
509 }
510 
511 /**
512  *****************************************************************************************
513  * @brief Encode a Body Composition measurement data.
514  *
515  * @param[in]  p_meas:      Pointer to the measurement data which to be encoded.
516  * @param[in]  max_payload: The max size of payload which is carried on ATT,
517  *                          equals (MTU - MEASUREMENT_VAL_MAX_LEN).
518  * @param[in]  cache_num:   The number of measurment caches.
519  *****************************************************************************************
520  */
bcs_meas_value_encoded(const bcs_meas_val_t * p_meas,uint16_t max_payload,uint8_t cache_num)521 static void bcs_meas_value_encoded(const bcs_meas_val_t *p_meas, uint16_t max_payload, uint8_t cache_num)
522 {
523     uint8_t packet_num = 0;
524 
525     for (uint8_t i = 0; i < cache_num; i++) {
526         meas_packet_t *p_pkt = &s_meas_packets.meas_packets[packet_num];
527         uint16_t      *p_flags;
528         uint8_t       *p_field;
529 
530         /** Put mandatory fields into packet. */
531         meas_packet_init(&p_meas[i], p_pkt, &p_flags, &p_field);
532 
533         /** The following are the optional fields. */
534         // Time Stamp Field.
535         if ((s_bcs_env.bcs_init.feature & BCS_FEAT_TIME_STAMP) && \
536                 s_bcs_env.bcs_init.bcs_meas_flags.time_stamp_present) {
537             *p_flags |= BCS_MEAS_FLAG_DATE_TIME_PRESENT;
538             p_field += prf_pack_date_time(p_field, &p_meas[i].time_stamp);
539         }
540 
541         // User ID Field.
542         if ((s_bcs_env.bcs_init.feature & BCS_FEAT_MULTI_USER) && \
543                 s_bcs_env.bcs_init.bcs_meas_flags.user_id_present) {
544             *p_flags |= BCS_MEAS_FLAG_USER_ID_PRESENT;
545             *p_field++ = p_meas[i].user_id;
546         }
547 
548         if (BCS_MEAS_UNSUCCESS != p_meas[i].body_fat_percentage) {
549             // Basal Metabolism Field.
550             if ((s_bcs_env.bcs_init.feature & BCS_FEAT_BASAL_METABOLISM) && \
551                     s_bcs_env.bcs_init.bcs_meas_flags.basal_metabolism_present) {
552                 packet_field_add(BCS_MEAS_FLAG_BASAL_METABOLISM, p_meas[i].basal_metabolism, p_flags, &p_field);
553             }
554 
555             // Muscle Percentage Field.
556             if ((s_bcs_env.bcs_init.feature & BCS_FEAT_MUSCLE_PERCENTAGE) && \
557                     s_bcs_env.bcs_init.bcs_meas_flags.muscle_percentage_present) {
558                 packet_field_add(BCS_MEAS_FLAG_MUSCLE_PERCENTAGE, p_meas[i].muscle_percentage, p_flags, &p_field);
559             }
560 
561             // Muscle Mass Field.
562             if ((s_bcs_env.bcs_init.feature & BCS_FEAT_MUSCLE_MASS) && \
563                     s_bcs_env.bcs_init.bcs_meas_flags.muscle_mass_present) {
564                 packet_field_add(BCS_MEAS_FLAG_MUSCLE_MASS, p_meas[i].muscle_mass, p_flags, &p_field);
565             }
566 
567             // Fat Free Mass Field.
568             if ((s_bcs_env.bcs_init.feature & BCS_FEAT_FAT_FREE_MASS) && \
569                     s_bcs_env.bcs_init.bcs_meas_flags.fat_free_mass_present) {
570                 packet_field_add(BCS_MEAS_FLAG_FAT_FREE_MASS, p_meas[i].fat_free_mass, p_flags, &p_field);
571             }
572 
573             p_pkt->size = p_field - p_pkt->value;
574 
575             /** If ATT_MTU=23 (max payload = 20), all above fields can be sent in one
576              *  packet, but for the remaining optional fields below the current packet
577              *  size shall be checked and the subsequent packet shall be used if needed.
578              */
579             // Soft Lean Mass Field.
580             if (!(s_bcs_env.bcs_init.feature & BCS_FEAT_SOFT_LEAN_MASS) || \
581                 !s_bcs_env.bcs_init.bcs_meas_flags.soft_lean_mass_present) {
582                 return;
583             }
584 
585             if (subsequent_packet_switched(max_payload, FIELD_SIZE, &s_meas_packets.meas_packets[packet_num]) == true) {
586                 p_pkt = &s_meas_packets.meas_packets[packet_num + 1];
587                 p_pkt->size = meas_packet_init(&p_meas[i], p_pkt, &p_flags, &p_field);
588             }
589 
590             p_pkt->size += packet_field_add(BCS_MEAS_FLAG_SOFT_LEAN_MASS,
591                                             p_meas[i].soft_lean_mass, p_flags, &p_field);
592             // Body Water Mass Field.
593             if (!(s_bcs_env.bcs_init.feature & BCS_FEAT_BODY_WATER_MASS) || \
594                 !s_bcs_env.bcs_init.bcs_meas_flags.body_water_mass_present) {
595                 return;
596             }
597 
598             if (subsequent_packet_switched(max_payload, FIELD_SIZE, &s_meas_packets.meas_packets[packet_num]) == true) {
599                 p_pkt = &s_meas_packets.meas_packets[packet_num + 1];
600                 p_pkt->size = meas_packet_init(&p_meas[i], p_pkt, &p_flags, &p_field);
601             }
602 
603             p_pkt->size += packet_field_add(BCS_MEAS_FLAG_BODY_WATER_MASS,
604                                             p_meas[i].body_water_mass, p_flags, &p_field);
605             // Impedance Field.
606             if (!(s_bcs_env.bcs_init.feature & BCS_FEAT_IMPEDANCE) || \
607                 !s_bcs_env.bcs_init.bcs_meas_flags.impedance_present) {
608                 return;
609             }
610             if (subsequent_packet_switched(max_payload, FIELD_SIZE, &s_meas_packets.meas_packets[packet_num]) == true) {
611                 p_pkt = &s_meas_packets.meas_packets[packet_num + 1];
612                 p_pkt->size = meas_packet_init(&p_meas[i], p_pkt, &p_flags, &p_field);
613             }
614 
615             p_pkt->size += packet_field_add(BCS_MEAS_FLAG_IMPEDANCE, p_meas[i].impedance, p_flags, &p_field);
616             // Weight Field.
617             if (!(s_bcs_env.bcs_init.feature & BCS_FEAT_WEIGHT) || \
618                 !s_bcs_env.bcs_init.bcs_meas_flags.weight_present) {
619                 return;
620             }
621             if (subsequent_packet_switched(max_payload, FIELD_SIZE,
622                                            &s_meas_packets.meas_packets[packet_num]) == true) {
623                 p_pkt = &s_meas_packets.meas_packets[packet_num + 1];
624                 p_pkt->size = meas_packet_init(&p_meas[i], p_pkt, &p_flags, &p_field);
625             }
626 
627             p_pkt->size += packet_field_add(BCS_MEAS_FLAG_WEIGHT, p_meas[i].weight, p_flags, &p_field);
628             // Height Field.
629             if (!(s_bcs_env.bcs_init.feature & BCS_FEAT_HEIGHT) || \
630                 !s_bcs_env.bcs_init.bcs_meas_flags.height_present) {
631                 return;
632             }
633             if  (subsequent_packet_switched(max_payload, LOAD_LEN, &s_meas_packets.meas_packets[packet_num]) == true) {
634                 p_pkt = &s_meas_packets.meas_packets[packet_num + 1];
635                 p_pkt->size = meas_packet_init(&p_meas[i], p_pkt, &p_flags, &p_field);
636             }
637 
638             p_pkt->size += packet_field_add(BCS_MEAS_FLAG_HEIGHT, p_meas[i].height, p_flags, &p_field);
639         }
640 
641         packet_num += PACKET_ADD_2;
642     }
643     s_meas_packets.packet_num = packet_num;
644     s_meas_packets.packet_to_be_send_num = packet_num;
645 }
646 
647 /*
648  * GLOBAL FUNCTION DEFINITIONS
649  *****************************************************************************************
650  */
bcs_measurement_send(uint8_t conn_idx,bcs_meas_val_t * p_bcs_meas_val,uint8_t cache_num)651 sdk_err_t bcs_measurement_send(uint8_t conn_idx, bcs_meas_val_t *p_bcs_meas_val, uint8_t cache_num)
652 {
653     if (p_bcs_meas_val == NULL || BCS_CACHE_MEAS_NUM_MAX < cache_num) {
654         return SDK_ERR_INVALID_PARAM;
655     }
656 
657     sdk_err_t        error_code = SDK_ERR_IND_DISABLED;
658 
659     error_code = memset_s(s_meas_packets.meas_packets, sizeof(s_meas_packets.meas_packets), \
660                           0x00, sizeof(s_meas_packets.meas_packets));
661     if (error_code < 0) {
662         return error_code;
663     }
664     s_meas_packets.packet_num            = 0;
665     s_meas_packets.packet_to_be_send_num = 0;
666 
667     if (PRF_CLI_START_IND == s_bcs_env.meas_ind_cfg[conn_idx]) {
668         bcs_meas_value_encoded(p_bcs_meas_val, BLE_ATT_MTU_DEFAULT - INDI_PAYLOAD_HEADER_LEN, cache_num);
669         error_code = bcs_indicate_meas_value_chunk(conn_idx);
670     }
671 
672     return error_code;
673 }
674 
bcs_service_init(bcs_init_t * p_bcs_init,uint16_t * p_bcs_start_handle)675 sdk_err_t bcs_service_init(bcs_init_t *p_bcs_init, uint16_t *p_bcs_start_handle)
676 {
677     sdk_err_t ret;
678     if (p_bcs_init == NULL) {
679         return SDK_ERR_POINTER_NULL;
680     }
681 
682     ret = memcpy_s(&s_bcs_env.bcs_init, sizeof(bcs_init_t), p_bcs_init, sizeof(bcs_init_t));
683     if (ret < 0) {
684         return ret;
685     }
686     s_bcs_env.p_start_handle = p_bcs_start_handle;
687 
688     return ble_server_prf_add(&bcs_prf_info);
689 }
690 
691