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