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