• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  ****************************************************************************************
3  *
4  * @file wss.c
5  *
6  * @brief Weight Scale Service implementation.
7  *
8  ****************************************************************************************
9  * @attention
10   #####Copyright (c) 2019 GOODIX
11   All rights reserved.
12 
13     Redistribution and use in source and binary forms, with or without
14     modification, are permitted provided that the following conditions are met:
15   * Redistributions of source code must retain the above copyright
16     notice, this list of conditions and the following disclaimer.
17   * Redistributions in binary form must reproduce the above copyright
18     notice, this list of conditions and the following disclaimer in the
19     documentation and/or other materials provided with the distribution.
20   * Neither the name of GOODIX nor the names of its contributors may be used
21     to endorse or promote products derived from this software without
22     specific prior written permission.
23 
24   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27   ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE
28   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34   POSSIBILITY OF SUCH DAMAGE.
35  *****************************************************************************************
36  */
37 
38 /*
39  * INCLUDE FILES
40  ****************************************************************************************
41  */
42 #include "wss.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 
49 #define WEIGHTS_SCALE 0.005
50 #define HEIGHT_SCALE 0.01
51 #define LB_KG 0.01
52 #define LB_KG_SCALE 0.4535924
53 #define TURN_TO_M 0.1
54 #define TURN_TO_M_SCALE 0.0254
55 #define PACKET_LEN_OFFSET 4
56 #define CACHE_NUM_LOW 25
57 #define SCALE_10 10
58 /*
59  * ENUMERATIONS
60  ****************************************************************************************
61  */
62 /**@brief Weight Scale Service Attributes Indexes. */
63 enum {
64     // Weight Scale Service
65     WSS_IDX_SVC,
66 
67     WSS_IDX_INCL_SVC,
68 
69     // WSS Scale Feature Characteristics
70     WSS_IDX_WS_FEAT_CHAR,
71     WSS_IDX_WS_FEAT_VAL,
72 
73     // Weight Measurement Characteristic
74     WSS_IDX_WEIGHT_MEAS_CHAR,
75     WSS_IDX_WEIGHT_MEAS_VAL,
76     WSS_IDX_WEIGHT_MEAS_IND_CFG,
77 
78     WSS_IDX_NB
79 };
80 
81 /*
82  * STRUCTURES
83  *****************************************************************************************
84  */
85 /**@brief Weight Scale measurement data stream. */
86 typedef struct {
87     uint8_t          *p_data;           /**< Pointer to data. */
88     uint16_t         length;            /**< Length of data. */
89     uint16_t         offset;            /**< Offset of data. */
90 } wss_meas_data_stream_t;
91 
92 /**@brief Weight Scale Service environment variable. */
93 struct wss_env_t {
94     wss_init_t               wss_init;                          /**< Weight Scale Service initialization variables. */
95     uint16_t                 start_hdl;                         /**< Weight Scale Service start handle. */
96     uint8_t                  cur_user_index;                    /**< Current User Index. */
97     uint16_t
98     meas_ind_cfg[WSS_CONNECTION_MAX];                   /**< The configuration of Weight Scale Indication
99                                                              which is configured by the peer devices. */
100     uint16_t                 *p_incl_srvc_start_handle;
101 };
102 
103 /*
104  * LOCAL FUNCTION DECLARATION
105  *****************************************************************************************
106  */
107 static sdk_err_t   wss_init(void);
108 static void        wss_read_att_cb(uint8_t conidx, const gatts_read_req_cb_t *p_param);
109 static void        wss_write_att_cb(uint8_t conidx, const gatts_write_req_cb_t *p_param);
110 static void        wss_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value);
111 static void        wss_gatts_ntf_ind_cb(uint8_t conn_idx, uint8_t status, const ble_gatts_ntf_ind_t *p_ntf_ind);
112 static uint16_t    wss_meas_value_encoded(wss_meas_val_t *p_wss_meas_val, uint8_t cache_num);
113 static uint16_t    wss_indicate_meas_value_chunk(uint8_t conn_idx);
114 
115 /*
116  * LOCAL VARIABLE DEFINITIONS
117  *****************************************************************************************
118  */
119 static struct wss_env_t        s_wss_env;
120 static wss_meas_data_stream_t  s_wss_meas_val;
121 static uint16_t                s_packet_length;
122 
123 /**@brief Full WSS Database Description - Used to add attributes into the database. */
124 static const attm_desc_t wss_attr_tab[WSS_IDX_NB] = {
125     // WSS Service Declaration
126     [WSS_IDX_SVC]                 = {BLE_ATT_DECL_PRIMARY_SERVICE, READ_PERM_UNSEC, 0, 0},
127 
128     // Include Service Declaration
129     [WSS_IDX_INCL_SVC]            = {BLE_ATT_DECL_INCLUDE, READ_PERM_UNSEC, 0, 0},
130 
131     // Weight Scale Feature Characteristic - Declaration
132     [WSS_IDX_WS_FEAT_CHAR]        = {BLE_ATT_DECL_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
133     // Weight Scale Feature Characteristic - Value
134     [WSS_IDX_WS_FEAT_VAL]         = {
135         BLE_ATT_CHAR_WEIGHT_SCALE_FEATURE,
136         READ_PERM_UNSEC,
137         ATT_VAL_LOC_USER,
138         WSS_FEAT_VAL_LEN_MAX
139     },
140 
141     // Weight Measurement Characteristic - Declaration
142     [WSS_IDX_WEIGHT_MEAS_CHAR]    = {BLE_ATT_DECL_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
143     // Weight Measurement Characteristic Declaration - value
144     [WSS_IDX_WEIGHT_MEAS_VAL]     = {
145         BLE_ATT_CHAR_WEIGHT_MEASUREMENT,
146         INDICATE_PERM_UNSEC,
147         ATT_VAL_LOC_USER,
148         WSS_MEAS_VAL_LEN_MAX
149     },
150     // Weight Measurement Declaration - Client Characteristic Configuration Descriptor
151     [WSS_IDX_WEIGHT_MEAS_IND_CFG] = {
152         BLE_ATT_DESC_CLIENT_CHAR_CFG,
153         READ_PERM_UNSEC | WRITE_REQ_PERM_UNSEC,
154         0,
155         0
156     },
157 };
158 
159 /**@brief WSS Task interface required by profile manager. */
160 static ble_prf_manager_cbs_t wss_task_cbs = {
161     (prf_init_func_t) wss_init,
162     NULL,
163     NULL
164 };
165 
166 /**@brief WSS Task Callbacks. */
167 static gatts_prf_cbs_t wss_cb_func = {
168     wss_read_att_cb,
169     wss_write_att_cb,
170     NULL,
171     wss_gatts_ntf_ind_cb,
172     wss_cccd_set_cb
173 };
174 
175 /**@brief WSS Information. */
176 static const prf_server_info_t wss_prf_info = {
177     .max_connection_nb = WSS_CONNECTION_MAX,
178     .manager_cbs       = &wss_task_cbs,
179     .gatts_prf_cbs     = &wss_cb_func
180 };
181 
182 /*
183  * LOCAL FUNCTION DEFINITIONS
184  *****************************************************************************************
185  */
186 /**
187  *****************************************************************************************
188  * @brief Initialize Weight Scale service and create db in att
189  *
190  * @return Error code to know if profile initialization succeed or not.
191  *****************************************************************************************
192  */
wss_init(void)193 static sdk_err_t wss_init(void)
194 {
195     // The start handle must be set with PRF_INVALID_HANDLE to be allocated automatically by BLE Stack.
196     uint16_t          start_hdl      = PRF_INVALID_HANDLE;
197     const uint8_t     wss_svc_uuid[] = BLE_ATT_16_TO_16_ARRAY(BLE_ATT_SVC_WEIGHT_SCALE);
198     sdk_err_t         error_code;
199     gatts_create_db_t gatts_db;
200 
201     error_code = memset_s(&gatts_db, sizeof(gatts_db), 0, sizeof(gatts_db));
202     if (error_code < 0) {
203         return error_code;
204     }
205 
206     gatts_db.shdl                 = &start_hdl;
207     gatts_db.uuid                 = wss_svc_uuid;
208     gatts_db.attr_tab_cfg         = (uint8_t *)&(s_wss_env.wss_init.char_mask);
209     gatts_db.max_nb_attr          = WSS_IDX_NB;
210     gatts_db.srvc_perm            = 0;
211     gatts_db.attr_tab_type        = SERVICE_TABLE_TYPE_16;
212     gatts_db.attr_tab.attr_tab_16 = wss_attr_tab;
213     gatts_db.inc_srvc_num         = 1;
214     gatts_db.inc_srvc_handle[0]   = s_wss_env.p_incl_srvc_start_handle;
215 
216     error_code = ble_gatts_srvc_db_create(&gatts_db);
217     if (SDK_SUCCESS == error_code) {
218         s_wss_env.start_hdl = *gatts_db.shdl;
219     }
220 
221     return error_code;
222 }
223 
224 /**
225  *****************************************************************************************
226  * @brief Handles reception of the attribute info request message.
227  *
228  * @param[in] conn_idx: Connection index
229  * @param[in] p_param:  The parameters of the read request.
230  *****************************************************************************************
231  */
wss_read_att_cb(uint8_t conn_idx,const gatts_read_req_cb_t * p_param)232 static void wss_read_att_cb(uint8_t conn_idx, const gatts_read_req_cb_t *p_param)
233 {
234     gatts_read_cfm_t  cfm;
235     uint8_t           handle    = p_param->handle;
236     uint8_t           tab_index = prf_find_idx_by_handle(handle,
237                                                          s_wss_env.start_hdl,
238                                                          WSS_IDX_NB,
239                                                          (uint8_t *)&s_wss_env.wss_init.char_mask);
240     cfm.handle = handle;
241     cfm.status = BLE_SUCCESS;
242 
243     switch (tab_index) {
244         case WSS_IDX_WS_FEAT_VAL: {
245             cfm.length = sizeof(uint32_t);
246             cfm.value  = (uint8_t *)&s_wss_env.wss_init.feature;
247             break;
248         }
249 
250         case WSS_IDX_WEIGHT_MEAS_IND_CFG: {
251             cfm.length = sizeof(uint16_t);
252             cfm.value  = (uint8_t *)&s_wss_env.meas_ind_cfg[conn_idx];
253             break;
254         }
255 
256         default: {
257             cfm.length = 0;
258             cfm.status = BLE_ATT_ERR_INVALID_HANDLE;
259             break;
260         }
261     }
262 
263     ble_gatts_read_cfm(conn_idx, &cfm);
264 }
265 
266 /**
267  *****************************************************************************************
268  * @brief Handles reception of the write request.
269  *
270  * @param[in]: conn_idx: Connection index
271  * @param[in]: p_param:  The parameters of the write request.
272  *****************************************************************************************
273  */
wss_write_att_cb(uint8_t conn_idx,const gatts_write_req_cb_t * p_param)274 static void   wss_write_att_cb(uint8_t conn_idx, const gatts_write_req_cb_t *p_param)
275 {
276     uint16_t          handle      = p_param->handle;
277     uint16_t          tab_index   = 0;
278     uint16_t          cccd_value  = 0;
279     wss_evt_t         event;
280     gatts_write_cfm_t cfm;
281 
282     tab_index      = prf_find_idx_by_handle(handle,
283                                             s_wss_env.start_hdl,
284                                             WSS_IDX_NB,
285                                             (uint8_t *)&s_wss_env.wss_init.char_mask);
286     cfm.handle     = handle;
287     cfm.status     = BLE_SUCCESS;
288     event.evt_type = WSS_EVT_INVALID;
289     event.conn_idx = conn_idx;
290 
291     switch (tab_index) {
292         case WSS_IDX_WEIGHT_MEAS_IND_CFG:
293             cccd_value     = le16toh(&p_param->value[0]);
294             event.evt_type = ((PRF_CLI_START_IND == cccd_value) ? \
295                               WSS_EVT_MEAS_INDICATION_ENABLE : \
296                               WSS_EVT_MEAS_INDICATION_DISABLE);
297             s_wss_env.meas_ind_cfg[conn_idx] = cccd_value;
298             break;
299 
300         default:
301             cfm.status = BLE_ATT_ERR_INVALID_HANDLE;
302             break;
303     }
304 
305     ble_gatts_write_cfm(conn_idx, &cfm);
306 
307     if (BLE_ATT_ERR_INVALID_HANDLE != cfm.status && WSS_EVT_INVALID != event.evt_type &&
308         s_wss_env.wss_init.evt_handler) {
309         s_wss_env.wss_init.evt_handler(&event);
310     }
311 }
312 
313 /**
314  *****************************************************************************************
315  * @brief Handles reception of the cccd recover request.
316  *
317  * @param[in]: conn_idx:   Connection index
318  * @param[in]: handle:     The handle of cccd attribute.
319  * @param[in]: cccd_value: The value of cccd attribute.
320  *****************************************************************************************
321  */
wss_cccd_set_cb(uint8_t conn_idx,uint16_t handle,uint16_t cccd_value)322 static void wss_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value)
323 {
324     uint16_t          tab_index   = 0;
325     wss_evt_t         event;
326 
327     if (!prf_is_cccd_value_valid(cccd_value)) {
328         return;
329     }
330 
331     tab_index  = prf_find_idx_by_handle(handle,
332                                         s_wss_env.start_hdl,
333                                         WSS_IDX_NB,
334                                         (uint8_t *)&s_wss_env.wss_init.char_mask);
335 
336     event.evt_type = WSS_EVT_INVALID;
337     event.conn_idx = conn_idx;
338 
339     switch (tab_index) {
340         case WSS_IDX_WEIGHT_MEAS_IND_CFG:
341             event.evt_type = ((PRF_CLI_START_NTF == cccd_value) ? \
342                               WSS_EVT_MEAS_INDICATION_ENABLE : \
343                               WSS_EVT_MEAS_INDICATION_DISABLE);
344             s_wss_env.meas_ind_cfg[conn_idx] = cccd_value;
345             break;
346 
347         default:
348             break;
349     }
350 
351     if (WSS_EVT_INVALID != event.evt_type && s_wss_env.wss_init.evt_handler) {
352         s_wss_env.wss_init.evt_handler(&event);
353     }
354 }
355 
356 /**
357  *****************************************************************************************
358  * @brief Handles reception of the complete event.
359  *
360  * @param[in] conn_idx:   Connection index.
361  * @param[in] status:     Complete event status.
362  * @param[in] p_ntf_ind:  Pointer to the parameters of the complete event.
363  *****************************************************************************************
364  */
wss_gatts_ntf_ind_cb(uint8_t conn_idx,uint8_t status,const ble_gatts_ntf_ind_t * p_ntf_ind)365 static void wss_gatts_ntf_ind_cb(uint8_t conn_idx, uint8_t status, const ble_gatts_ntf_ind_t *p_ntf_ind)
366 {
367     if (s_wss_env.wss_init.evt_handler && SDK_SUCCESS == status && s_wss_env.meas_ind_cfg[conn_idx]) {
368         if (BLE_GATT_INDICATION == p_ntf_ind->type) {
369             wss_indicate_meas_value_chunk(conn_idx);
370         }
371     }
372 }
373 
374 /**
375  *****************************************************************************************
376  * @brief Handle Weight Measurement data indication.
377  *
378  * @param[in] conn_idx: Connection index.
379  *
380  * @return Result of handle.
381  *****************************************************************************************
382  */
wss_indicate_meas_value_chunk(uint8_t conn_idx)383 static uint16_t wss_indicate_meas_value_chunk(uint8_t conn_idx)
384 {
385     uint16_t         chunk_len;
386     gatts_noti_ind_t wss_meas_ind;
387     sdk_err_t        error_code;
388 
389     chunk_len = s_wss_meas_val.length - s_wss_meas_val.offset;
390     chunk_len = chunk_len > s_packet_length ? s_packet_length : chunk_len;
391 
392     if (chunk_len == 0) {
393         s_wss_meas_val.p_data = NULL;
394         s_wss_meas_val.length = 0;
395         s_wss_meas_val.offset = 0;
396 
397         return SDK_SUCCESS;
398     }
399 
400     wss_meas_ind.type   = BLE_GATT_INDICATION;
401     wss_meas_ind.handle = prf_find_handle_by_idx(WSS_IDX_WEIGHT_MEAS_VAL,
402                                                  s_wss_env.start_hdl,
403                                                  (uint8_t *)&s_wss_env.wss_init.char_mask);
404     wss_meas_ind.length = chunk_len;
405     wss_meas_ind.value  = (uint8_t *)s_wss_meas_val.p_data + s_wss_meas_val.offset;
406 
407     error_code = ble_gatts_noti_ind(conn_idx, &wss_meas_ind);
408     if (SDK_SUCCESS == error_code) {
409         s_wss_meas_val.offset += chunk_len;
410     }
411 
412     return error_code;
413 }
414 
415 /**
416  *****************************************************************************************
417  * @brief Encoding a Weight Scale Measurement.
418  *
419  * @param[in]  p_wss_meas_val: Pointer to WS measurement value to be encoded.
420  * @param[in]  cache_num:      The number of measurment caches.
421  *
422  * @return Length of encoded data.
423  *****************************************************************************************
424  */
wss_meas_value_encoded(wss_meas_val_t * p_wss_meas_val,uint8_t cache_num)425 static uint16_t wss_meas_value_encoded(wss_meas_val_t *p_wss_meas_val, uint8_t cache_num)
426 {
427     uint8_t local_buff[WSS_MEAS_VAL_LEN_MAX * WSS_CACHE_MEAS_NUM_MAX] = {0};
428     uint8_t  flags  = 0;
429     uint16_t length = 0;
430     double   height_in_m;
431     double   weight_in_kg;
432 
433     s_wss_meas_val.length = 0;
434 
435     for (uint8_t i = 0; i < cache_num; i++) {
436         length++;
437         if (WSS_MEAS_UNSUCCESS != p_wss_meas_val->weight) {
438             // Weight Field
439             local_buff[length++] = LO_U16(p_wss_meas_val[i].weight);
440             local_buff[length++] = HI_U16(p_wss_meas_val[i].weight);
441         } else {
442             // Weight Field
443             local_buff[length++] = LO_U16(WSS_MEAS_UNSUCCESS);
444             local_buff[length++] = HI_U16(WSS_MEAS_UNSUCCESS);
445         }
446 
447         // Time Stamp Field
448         if ((s_wss_env.wss_init.feature & WSS_FEAT_TIME_STAMP) && (s_wss_env.wss_init.time_stamp_present)) {
449             local_buff[length++] = LO_U16(p_wss_meas_val[i].time_stamp.year);
450             local_buff[length++] = HI_U16(p_wss_meas_val[i].time_stamp.year);
451             local_buff[length++] = p_wss_meas_val[i].time_stamp.month;
452             local_buff[length++] = p_wss_meas_val[i].time_stamp.day;
453             local_buff[length++] = p_wss_meas_val[i].time_stamp.hour;
454             local_buff[length++] = p_wss_meas_val[i].time_stamp.min;
455             local_buff[length++] = p_wss_meas_val[i].time_stamp.sec;
456             flags |= WSS_MEAS_FLAG_DATE_TIME_PRESENT;
457         }
458 
459         // User ID Field
460         if ((s_wss_env.wss_init.feature & WSS_FEAT_MULTI_USER) && (s_wss_env.wss_init.multi_user_present)) {
461             local_buff[length++] = p_wss_meas_val[i].user_id;
462             flags |= WSS_MEAS_FLAG_USER_ID_PRESENT;
463         }
464 
465         if (WSS_MEAS_UNSUCCESS != p_wss_meas_val->weight) {
466             // BMI and Height Fields
467             if ((WSS_FEAT_BMI & s_wss_env.wss_init.feature) && (s_wss_env.wss_init.bmi_present)) {
468                 uint16_t bmi;
469 
470                 s_packet_length = WSS_MEAS_VAL_LEN_MAX;
471 
472                 if (WSS_UNIT_SI == s_wss_env.wss_init.wss_unit) {
473                     if ((WSS_FEAT_MASS_RES_5G & s_wss_env.wss_init.feature) && \
474                         (WSS_FEAT_HEIGHT_RES_1MM & s_wss_env.wss_init.feature) && \
475                         (WSS_HEIGHT_RES_1MM == s_wss_env.wss_init.wss_height_res) && \
476                         (WSS_MASS_RES_5G == s_wss_env.wss_init.wss_mass_res)) {
477                         weight_in_kg = p_wss_meas_val[i].weight * WEIGHTS_SCALE;
478                         height_in_m  = p_wss_meas_val->height * HEIGHT_SCALE;
479                         bmi = (weight_in_kg/(height_in_m * height_in_m)) * SCALE_10;
480                     }
481                 } else {
482                     if ((WSS_FEAT_MASS_RES_5G & s_wss_env.wss_init.feature) && \
483                         (WSS_FEAT_HEIGHT_RES_1MM & s_wss_env.wss_init.feature) && \
484                         (WSS_HEIGHT_RES_1MM == s_wss_env.wss_init.wss_height_res) && \
485                         (WSS_MASS_RES_5G == s_wss_env.wss_init.wss_mass_res)) {
486                         weight_in_kg = p_wss_meas_val[i].weight * LB_KG * LB_KG_SCALE; // turn lb to kg.
487                         height_in_m  = p_wss_meas_val->height * TURN_TO_M * TURN_TO_M_SCALE; // turn in to m.
488                         bmi = (weight_in_kg/(height_in_m * height_in_m)) * SCALE_10;
489                     }
490                 }
491                 local_buff[length++] = LO_U16(bmi);
492                 local_buff[length++] = HI_U16(bmi);
493                 local_buff[length++] = LO_U16(p_wss_meas_val->height * SCALE_10);
494                 local_buff[length++] = HI_U16(p_wss_meas_val->height * SCALE_10);
495 
496                 flags |= WSS_MEAS_FLAG_BMI_HEIGHT_PRESENT;
497             } else if ((WSS_FEAT_BMI & s_wss_env.wss_init.feature) && (!s_wss_env.wss_init.bmi_present)) {
498                 s_packet_length = WSS_MEAS_VAL_LEN_MAX - PACKET_LEN_OFFSET;
499             }
500         } else {
501             s_packet_length = WSS_MEAS_VAL_LEN_MAX - PACKET_LEN_OFFSET;
502         }
503 
504         if (WSS_UNIT_SI == s_wss_env.wss_init.wss_unit) {
505             flags |= WSS_MEAS_FLAG_UNIT_SI;
506         } else {
507             flags |= WSS_MEAS_FLAG_UNIT_IMPERIAL;
508         }
509 
510         local_buff[s_wss_meas_val.length] = flags;
511         s_wss_meas_val.length = length;
512     }
513 
514     s_wss_meas_val.offset = 0;
515     s_wss_meas_val.p_data = &local_buff[0];
516 
517     return length;
518 }
519 
520 /*
521  * GLOBAL FUNCTION DEFINITIONS
522  *****************************************************************************************
523  */
wss_measurement_send(uint8_t conn_idx,wss_meas_val_t * p_wss_meas_val,uint8_t cache_num)524 sdk_err_t wss_measurement_send(uint8_t conn_idx, wss_meas_val_t *p_wss_meas_val, uint8_t cache_num)
525 {
526     if (p_wss_meas_val == NULL || cache_num > CACHE_NUM_LOW) {
527         return SDK_ERR_INVALID_PARAM;
528     }
529 
530     sdk_err_t error_code = SDK_ERR_IND_DISABLED;
531 
532     if (PRF_CLI_START_IND == s_wss_env.meas_ind_cfg[conn_idx]) {
533         wss_meas_value_encoded(p_wss_meas_val, cache_num);
534         error_code = wss_indicate_meas_value_chunk(conn_idx);
535     }
536 
537     return error_code;
538 }
539 
wss_service_init(wss_init_t * p_wss_init,uint16_t * p_incl_srvc_start_handle)540 sdk_err_t wss_service_init(wss_init_t *p_wss_init, uint16_t *p_incl_srvc_start_handle)
541 {
542     sdk_err_t ret;
543     if (p_wss_init == NULL) {
544         return SDK_ERR_POINTER_NULL;
545     }
546 
547     ret = memset_S(&s_wss_env, sizeof(s_wss_env), 0, sizeof(s_wss_env));
548     if (ret < 0) {
549         return ret;
550     }
551     ret = memcpy_s(&s_wss_env.wss_init, sizeof(wss_init_t), p_wss_init, sizeof(wss_init_t));
552     if (ret < 0) {
553         return ret;
554     }
555     s_wss_env.p_incl_srvc_start_handle = p_incl_srvc_start_handle;
556 
557     return ble_server_prf_add(&wss_prf_info);
558 }
559