• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  *****************************************************************************************
3  *
4  * @file dss.c
5  *
6  * @brief Device Synchronize 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 "dss.h"
43 #include "ble_prf_types.h"
44 #include "ble_prf_utils.h"
45 #include "utility.h"
46 #include "app_error.h"
47 #include "app_log.h"
48 
49 /*
50  * DEFINES
51  *****************************************************************************************
52  */
53 /**@brief The UUIDs of DSS service and characteristics. */
54 #define DSS_SERVICE_UUID       {0x1B, 0xD7, 0x90, 0xEC, 0xE8, 0xB9, 0x75, 0x80, 0x0A, 0x46, 0x44, 0xD3,
55                                 0x01, 0x0A, 0xED, 0xA6}
56 #define DSS_ROLE_UUID          {0x1B, 0xD7, 0x90, 0xEC, 0xE8, 0xB9, 0x75, 0x80, 0x0A, 0x46, 0x44, 0xD3,
57                                 0x02, 0x0A, 0xED, 0xA6}
58 #define DSS_EVT_CNT_UUID       {0x1B, 0xD7, 0x90, 0xEC, 0xE8, 0xB9, 0x75, 0x80, 0x0A, 0x46, 0x44, 0xD3,
59                                 0x03, 0x0A, 0xED, 0xA6}
60 #define DSS_EVT_PERIOD_UUID    {0x1B, 0xD7, 0x90, 0xEC, 0xE8, 0xB9, 0x75, 0x80, 0x0A, 0x46, 0x44, 0xD3,
61                                 0x04, 0x0A, 0xED, 0xA6}
62 #define DSS_STATUS_UUID        {0x1B, 0xD7, 0x90, 0xEC, 0xE8, 0xB9, 0x75, 0x80, 0x0A, 0x46, 0x44, 0xD3,
63                                 0x05, 0x0A, 0xED, 0xA6}
64 #define DSS_CTRL_PT_UUID       {0x1B, 0xD7, 0x90, 0xEC, 0xE8, 0xB9, 0x75, 0x80, 0x0A, 0x46, 0x44, 0xD3,
65                                 0x06, 0x0A, 0xED, 0xA6}
66 
67 /**@brief Macros for conversion of 128bit to 16bit UUID. */
68 #define ATT_128_PRIMARY_SERVICE BLE_ATT_16_TO_128_ARRAY(BLE_ATT_DECL_PRIMARY_SERVICE)
69 #define ATT_128_CHARACTERISTIC  BLE_ATT_16_TO_128_ARRAY(BLE_ATT_DECL_CHARACTERISTIC)
70 #define ATT_128_CLIENT_CHAR_CFG BLE_ATT_16_TO_128_ARRAY(BLE_ATT_DESC_CLIENT_CHAR_CFG)
71 
72 /*
73  * ENUMERATIONS
74  *****************************************************************************************
75  */
76 /**@brief Device Synchronize Service Attributes database index list. */
77 enum dss_attr_idx_t {
78     DSS_IDX_SVC,
79 
80     DSS_IDX_ROLE_CHAR,
81     DSS_IDX_ROLE_VAL,
82 
83     DSS_IDX_SYNC_CNT_CHAR,
84     DSS_IDX_SYNC_CNT_VAL,
85     DSS_IDX_SYNC_CNT_CFG,
86 
87     DSS_IDX_SYNC_PERIOD_CHAR,
88     DSS_IDX_SYNC_PERIOD_VAL,
89 
90     DSS_IDX_SYNC_STATUS_CHAR,
91     DSS_IDX_SYNC_STATUS_VAL,
92 
93     DSS_IDX_CTRL_PT_CHAR,
94     DSS_IDX_CTRL_PT_VAL,
95     DSS_IDX_CTRL_PT_CFG,
96 
97     DSS_IDX_NB,
98 };
99 
100 /*
101  * STRUCTURES
102  *****************************************************************************************
103  */
104 /**@brief Device Synchronize Service environment variable. */
105 struct dss_env_t {
106     dss_evt_handler_t   evt_handler;
107     uint16_t            start_hdl;
108     uint16_t            char_mask;
109     dss_role_t          dss_role;
110     dss_staus_t         sync_status;
111     uint16_t            event_period;
112     uint32_t            sync_cnt;
113     uint8_t             sync_cfg_conn_idx;
114     bool                is_busy_send;
115     bool                is_auto_calib_drift;
116     bool                is_auto_enter_lp;
117     bool                is_in_lp;
118     uint32_t            auto_calib_timing;
119     uint8_t             sync_device_num;
120     uint16_t            evt_cnt_ntf[DSS_CONNECTION_MAX];
121     uint16_t            ctrl_pt_ind[DSS_CONNECTION_MAX];
122 };
123 
124 /*
125  * LOCAL FUNCTION DECLARATION
126  *****************************************************************************************
127  */
128 static sdk_err_t dss_init(void);
129 static void      dss_disconnect_cb(uint8_t conn_idx, uint8_t reason);
130 static void      dss_read_att_cb(uint8_t conn_idx, const gatts_read_req_cb_t *p_param);
131 static void      dss_write_att_cb(uint8_t conn_idx, const gatts_write_req_cb_t *p_param);
132 static void      dss_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value);
133 static void      dss_ntf_ind_cb(uint8_t conn_idx, uint8_t status, const ble_gatts_ntf_ind_t *p_ntf_ind);
134 static void      dss_ctrl_pt_handler(uint8_t conn_idx, const uint8_t *p_data, uint16_t length);
135 static sdk_err_t dss_ctrl_pt_rsp_send(uint8_t conn_idx, dss_op_id_t op_id, dss_rsp_id_t rsp_id);
136 /*
137  * LOCAL VARIABLE DEFINITIONS
138  *****************************************************************************************
139  */
140 static struct dss_env_t s_dss_env;
141 
142 /**@brief Full DSS Database Description which is used to add attributes into the ATT database. */
143 static const attm_desc_128_t dss_attr_tab[DSS_IDX_NB] = {
144     [DSS_IDX_SVC]              = {ATT_128_PRIMARY_SERVICE, READ_PERM_UNSEC, 0, 0},
145 
146     [DSS_IDX_ROLE_CHAR]        = {ATT_128_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
147     [DSS_IDX_ROLE_VAL]         = {
148         DSS_ROLE_UUID,
149         READ_PERM_UNSEC,
150         ATT_VAL_LOC_USER | ATT_UUID_TYPE_SET(UUID_TYPE_128),
151         DSS_ROLE_VALUE_LEN
152     },
153 
154     [DSS_IDX_SYNC_CNT_CHAR]     = {ATT_128_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
155     [DSS_IDX_SYNC_CNT_VAL]      = {
156         DSS_EVT_CNT_UUID,
157         READ_PERM_UNSEC | NOTIFY_PERM_UNSEC,
158         ATT_VAL_LOC_USER | ATT_UUID_TYPE_SET(UUID_TYPE_128),
159         DSS_EVT_CNT_VALUE_LEN
160     },
161     [DSS_IDX_SYNC_CNT_CFG]      = {ATT_128_CLIENT_CHAR_CFG, READ_PERM_UNSEC | WRITE_REQ_PERM_UNSEC, 0, 0},
162 
163     [DSS_IDX_SYNC_PERIOD_CHAR]  = {ATT_128_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
164     [DSS_IDX_SYNC_PERIOD_VAL]   = {
165         DSS_EVT_PERIOD_UUID,
166         READ_PERM_UNSEC,
167         ATT_VAL_LOC_USER | ATT_UUID_TYPE_SET(UUID_TYPE_128),
168         DSS_EVT_PERIOD_VALUE_LEN
169     },
170 
171     [DSS_IDX_SYNC_STATUS_CHAR] = {ATT_128_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
172     [DSS_IDX_SYNC_STATUS_VAL]  = {
173         DSS_STATUS_UUID,
174         READ_PERM_UNSEC,
175         ATT_VAL_LOC_USER  | ATT_UUID_TYPE_SET(UUID_TYPE_128),
176         DSS_STATUS_VALUE_LEN
177     },
178 
179     [DSS_IDX_CTRL_PT_CHAR]     = {ATT_128_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
180     [DSS_IDX_CTRL_PT_VAL]      = {
181         DSS_CTRL_PT_UUID,
182         WRITE_REQ_PERM_UNSEC | INDICATE_PERM_UNSEC,
183         ATT_VAL_LOC_USER  | ATT_UUID_TYPE_SET(UUID_TYPE_128),
184         DSS_CTRL_PT_VALUE_LEN
185     },
186     [DSS_IDX_CTRL_PT_CFG]      = {ATT_128_CLIENT_CHAR_CFG, READ_PERM_UNSEC | WRITE_REQ_PERM_UNSEC, 0, 0},
187 };
188 
189 /**@brief Device Synchronize Service interface required by profile manager. */
190 static ble_prf_manager_cbs_t dss_mgr_cbs = {
191     (prf_init_func_t)dss_init,
192     NULL,
193     dss_disconnect_cb
194 };
195 
196 /**@brief Device Synchronize GATT Server Callbacks. */
197 static gatts_prf_cbs_t dss_gatts_cbs = {
198     dss_read_att_cb,
199     dss_write_att_cb,
200     NULL,
201     dss_ntf_ind_cb,
202     dss_cccd_set_cb
203 };
204 
205 /**@brief Device Synchronize Service Information. */
206 static const prf_server_info_t dss_prf_info = {
207     .max_connection_nb = DSS_CONNECTION_MAX,
208     .manager_cbs       = &dss_mgr_cbs,
209     .gatts_prf_cbs     = &dss_gatts_cbs
210 };
211 
212 /*
213  * LOCAL FUNCTION DEFINITIONS
214  *****************************************************************************************
215  */
216 /**
217  *****************************************************************************************
218  * @brief Initialize Device Synchronize Service and create DB in ATT.
219  *
220  * @return Error code to know if service initialization succeed or not.
221  *****************************************************************************************
222  */
223 static sdk_err_t dss_init(void)
224 {
225     // The start hanlde must be set with PRF_INVALID_HANDLE to be allocated automatically by BLE Stack.
226     uint16_t          start_hdl      = PRF_INVALID_HANDLE;
227     const uint8_t     dss_svc_uuid[] = DSS_SERVICE_UUID;
228     sdk_err_t         error_code     = SDK_SUCCESS;
229     gatts_create_db_t gatts_db;
230 
231     error_code = memset_s(&gatts_db, sizeof(gatts_db), 0, sizeof(gatts_db));
232     if (error_code < 0) {
233         return error_code;
234     }
235 
236     gatts_db.shdl                  = &start_hdl;
237     gatts_db.uuid                  = dss_svc_uuid;
238     gatts_db.attr_tab_cfg          = (uint8_t *)&s_dss_env.char_mask;
239     gatts_db.max_nb_attr           = DSS_IDX_NB;
240     gatts_db.srvc_perm             = SRVC_UUID_TYPE_SET(UUID_TYPE_128);
241     gatts_db.attr_tab_type         = SERVICE_TABLE_TYPE_128;
242     gatts_db.attr_tab.attr_tab_128 = dss_attr_tab;
243 
244     error_code = ble_gatts_srvc_db_create(&gatts_db);
245     if (SDK_SUCCESS == error_code) {
246         s_dss_env.start_hdl = *gatts_db.shdl;
247     }
248 
249     return error_code;
250 }
251 
252 /**
253  *****************************************************************************************
254  * @brief Device Synchronize Service disconnect callback.
255  *****************************************************************************************
256  */
257 static void dss_disconnect_cb(uint8_t conn_idx, uint8_t reason)
258 {
259     s_dss_env.evt_cnt_ntf[conn_idx] = 0x0000;
260     s_dss_env.ctrl_pt_ind[conn_idx] = 0x0000;
261 
262     if (conn_idx == s_dss_env.sync_cfg_conn_idx) {
263         s_dss_env.sync_cfg_conn_idx = GAP_INVALID_CONN_INDEX;
264     }
265 }
266 
267 /**
268  *****************************************************************************************
269  * @brief Handles reception of the attribute info request message.
270  *
271  * @param[in] conn_idx: Connection index
272  * @param[in] p_param:  Pointer to the parameters of the read request.
273  *****************************************************************************************
274  */
275 static void dss_read_att_cb(uint8_t conn_idx, const gatts_read_req_cb_t *p_param)
276 {
277     gatts_read_cfm_t cfm;
278     uint16_t         handle    = p_param->handle;
279     uint8_t          tab_index = prf_find_idx_by_handle(handle,
280                                  s_dss_env.start_hdl,
281                                  DSS_IDX_NB,
282                                  (uint8_t *)&s_dss_env.char_mask);
283     cfm.handle = handle;
284     cfm.status = BLE_SUCCESS;
285 
286     switch (tab_index) {
287         case DSS_IDX_ROLE_VAL:
288             cfm.length = DSS_ROLE_VALUE_LEN;
289             cfm.value  = (uint8_t *)&s_dss_env.dss_role;
290             break;
291 
292         case DSS_IDX_SYNC_CNT_VAL:
293             cfm.length = DSS_EVT_CNT_VALUE_LEN;
294             cfm.value  = (uint8_t *)&s_dss_env.sync_cnt;
295             break;
296 
297         case DSS_IDX_SYNC_CNT_CFG:
298             cfm.length = sizeof(uint16_t);
299             cfm.value  = (uint8_t *)&s_dss_env.evt_cnt_ntf[conn_idx];
300             break;
301 
302         case DSS_IDX_SYNC_PERIOD_VAL:
303             cfm.length = DSS_EVT_PERIOD_VALUE_LEN;
304             cfm.value  = (uint8_t *)&s_dss_env.event_period;
305             break;
306 
307         case DSS_IDX_SYNC_STATUS_VAL:
308             cfm.length = DSS_STATUS_VALUE_LEN;
309             cfm.value  = (uint8_t *)&s_dss_env.sync_status;
310             break;
311 
312         case DSS_IDX_CTRL_PT_CFG:
313             cfm.length = sizeof(uint16_t);
314             cfm.value  = (uint8_t *)&s_dss_env.ctrl_pt_ind[conn_idx];
315             break;
316 
317         default:
318             cfm.length = 0;
319             cfm.status = BLE_ATT_ERR_INVALID_HANDLE;
320             break;
321     }
322 
323     ble_gatts_read_cfm(conn_idx, &cfm);
324 }
325 
326 
327 /**
328  *****************************************************************************************
329  * @brief Handles reception of the write request.
330  *
331  * @param[in] conn_idx: Index of the connection.
332  * @param[in] p_param:  Point to the parameters of the write request.
333  *****************************************************************************************
334  */
335 static void dss_write_att_cb(uint8_t conn_idx, const gatts_write_req_cb_t *p_param)
336 {
337     uint8_t           handle    = p_param->handle;
338     uint8_t           tab_index = 0;
339     gatts_write_cfm_t cfm;
340     bool              ctrl_pt_evt = false;
341 
342     s_dss_env.sync_cfg_conn_idx = conn_idx;
343     tab_index  = prf_find_idx_by_handle(handle,
344                                         s_dss_env.start_hdl,
345                                         DSS_IDX_NB,
346                                         (uint8_t *)&s_dss_env.char_mask);
347     cfm.handle = handle;
348     cfm.status = BLE_SUCCESS;
349 
350     switch (tab_index) {
351         case DSS_IDX_SYNC_CNT_CFG:
352             s_dss_env.evt_cnt_ntf[conn_idx] = le16toh(&p_param->value[0]);
353             break;
354 
355         case DSS_IDX_CTRL_PT_VAL:
356             ctrl_pt_evt = true;
357             break;
358 
359         case DSS_IDX_CTRL_PT_CFG:
360             s_dss_env.ctrl_pt_ind[conn_idx] = le16toh(&p_param->value[0]);
361             break;
362 
363         default:
364             cfm.status = BLE_ATT_ERR_INVALID_HANDLE;
365             break;
366     }
367 
368     ble_gatts_write_cfm(conn_idx, &cfm);
369 
370     if (ctrl_pt_evt) {
371         dss_ctrl_pt_handler(conn_idx, p_param->value, p_param->length);
372     }
373 }
374 
375 /**
376  *****************************************************************************************
377  * @brief Handles reception of the cccd recover request.
378  *
379  * @param[in]: conn_idx:   Connection index
380  * @param[in]: handle:     The handle of cccd attribute.
381  * @param[in]: cccd_value: The value of cccd attribute.
382  *****************************************************************************************
383  */
384 static void dss_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value)
385 {
386     uint8_t tab_index = 0;
387 
388     if (!prf_is_cccd_value_valid(cccd_value)) {
389         return;
390     }
391 
392     tab_index = prf_find_idx_by_handle(handle,
393                                        s_dss_env.start_hdl,
394                                        DSS_IDX_NB,
395                                        (uint8_t *)&s_dss_env.char_mask);
396 
397     switch (tab_index) {
398         case DSS_IDX_SYNC_CNT_CFG:
399             s_dss_env.evt_cnt_ntf[conn_idx] = cccd_value;
400             break;
401 
402         case DSS_IDX_CTRL_PT_CFG:
403             s_dss_env.ctrl_pt_ind[conn_idx] = cccd_value;
404             break;
405 
406         default:
407             break;
408     }
409 }
410 
411 /**
412  *****************************************************************************************
413  * @brief Handles reception of the complete event.
414  *
415  * @param[in] conn_idx:   Connection index.
416  * @param[in] status:     The status of GATTC operation.
417  * @param[in] p_ntf_ind:  Pointer to the parameters of the complete event.
418  *****************************************************************************************
419  */
420 static void dss_ntf_ind_cb(uint8_t conn_idx, uint8_t status, const ble_gatts_ntf_ind_t *p_ntf_ind)
421 {
422     if (BLE_SUCCESS == status && BLE_GATT_NOTIFICATION == p_ntf_ind->type) {
423         s_dss_env.is_busy_send = false;
424     } else if (status && BLE_GATT_NOTIFICATION == p_ntf_ind->type) {
425         s_dss_env.is_busy_send = true;
426     }
427 }
428 
429 
430 /**
431  *****************************************************************************************
432  * @brief Set role OP handler.
433  *
434  * @param[in] conn_idx: Connection index.
435  * @param[in] p_data:   Pointer to data.
436  * @param[in] length:   Length of data.
437  *****************************************************************************************
438  */
439 static void dss_op_role_set_handler(uint8_t conn_idx, const uint8_t *p_data, uint16_t length)
440 {
441     dss_rsp_id_t rsp_id = DSS_RSP_ID_SUCCESS;
442     dss_evt_t      evt;
443 
444     if (length != 2 || (p_data[1] != DSS_ROLE_SYNC_SOURCE && p_data[1] != DSS_ROLE_SYNC_DEVICE)) {
445         rsp_id = DSS_RSP_ID_PARAM_ERR;
446     } else if (s_dss_env.sync_status != DSS_STATUS_CFG_READY) {
447         rsp_id = DSS_RSP_ID_STATUS_ERR;
448     } else {
449         s_dss_env.dss_role = (dss_role_t)p_data[1];
450 
451         evt.evt_type = s_dss_env.dss_role == DSS_ROLE_SYNC_SOURCE ? DSS_EVT_SOURCE_ROLE_SET : DSS_EVT_DEVICE_ROLE_SET;
452         s_dss_env.evt_handler(&evt);
453     }
454 
455     dss_ctrl_pt_rsp_send(conn_idx, DSS_OP_ID_ROLE_SET, rsp_id);
456 }
457 
458 /**
459  *****************************************************************************************
460  * @brief Create sync source OP handler.
461  *
462  * @param[in] conn_idx: Connection index.
463  * @param[in] p_data:   Pointer to data.
464  * @param[in] length:   Length of data.
465  *****************************************************************************************
466  */
467 static void dss_op_sync_src_create_handler(uint8_t conn_idx, const uint8_t *p_data, uint16_t length)
468 {
469     dss_rsp_id_t rsp_id = DSS_RSP_ID_SUCCESS;
470     uint16_t     event_period = le16toh(&p_data[1]);
471     dss_evt_t    evt;
472 
473     if (length != 3 || (le16toh(&p_data[1]) < 32) || (le16toh(&p_data[1]) > 3200)) {
474         rsp_id = DSS_RSP_ID_PARAM_ERR;
475     } else if (s_dss_env.sync_status != DSS_STATUS_CFG_READY) {
476         rsp_id = DSS_RSP_ID_STATUS_ERR;
477     } else if (s_dss_env.dss_role != DSS_ROLE_SYNC_SOURCE) {
478         rsp_id = DSS_RSP_ID_ROLE_ERR;
479     } else {
480         if (ble_sync_source_create(event_period)) {
481             rsp_id = DSS_RSP_ID_CREATE_SRC_FAIL;
482         } else {
483             s_dss_env.sync_status         = DSS_STATUS_CFG_READY;
484             s_dss_env.event_period        = event_period;
485 
486             evt.evt_type = DSS_EVT_SYNC_SRC_CREATE;
487             s_dss_env.evt_handler(&evt);
488         }
489     }
490 
491     dss_ctrl_pt_rsp_send(conn_idx, DSS_OP_ID_SYNC_SRC_CREATE, rsp_id);
492 }
493 
494 
495 /**
496  *****************************************************************************************
497  * @brief Synchronize handler.
498  *
499  * @param[in] conn_idx: Connection index.
500  * @param[in] p_data:   Pointer to data.
501  * @param[in] length:   Length of data.
502  *****************************************************************************************
503  */
504 static void dss_op_sync_handler(uint8_t conn_idx, const uint8_t *p_data, uint16_t length, dss_op_id_t op_id)
505 {
506     dss_rsp_id_t   rsp_id = DSS_RSP_ID_SUCCESS;
507     dss_evt_t      evt;
508 
509     if ((op_id != DSS_OP_ID_SYNC) || length != 7 || p_data[6] == 0) {
510         rsp_id = DSS_RSP_ID_PARAM_ERR;
511     } else if (s_dss_env.sync_status != DSS_STATUS_CFG_READY) {
512         rsp_id = DSS_RSP_ID_STATUS_ERR;
513     } else if (!s_dss_env.event_period && s_dss_env.dss_role == DSS_ROLE_SYNC_SOURCE) {
514         rsp_id = DSS_RSP_ID_DISALLOWED;
515     } else if (s_dss_env.dss_role == DSS_ROLE_SYNC_INVALID) {
516         rsp_id = DSS_RSP_ID_ROLE_ERR;
517     } else if (!s_dss_env.evt_handler) {
518         rsp_id = DSS_RSP_ID_NO_HANDLER;
519     } else {
520         s_dss_env.is_auto_calib_drift  = (p_data[1] & 0x01) ? true : false;
521         s_dss_env.is_auto_enter_lp     = (p_data[1] & 0x02) ? true : false;
522 
523         s_dss_env.auto_calib_timing    = BUILD_U32(p_data[2], p_data[3], p_data[4], p_data[5]);
524         s_dss_env.sync_device_num      = p_data[6];
525 
526         if (s_dss_env.is_auto_calib_drift && !s_dss_env.auto_calib_timing) {
527             s_dss_env.is_auto_calib_drift  = false;
528             s_dss_env.is_auto_enter_lp     = false;
529             rsp_id = DSS_RSP_ID_PARAM_ERR;
530         } else {
531             s_dss_env.is_in_lp    = false;
532             s_dss_env.sync_status = s_dss_env.dss_role == DSS_ROLE_SYNC_SOURCE ? DSS_STATUS_IN_SCAN : DSS_STATUS_IN_ADV;
533 
534             evt.conn_idx          = conn_idx;
535             evt.evt_type          = DSS_EVT_SYNC_SELF_OR_PEER;
536             evt.is_enter_lp_mode  = s_dss_env.is_auto_enter_lp;
537             evt.sync_dev_num      = s_dss_env.sync_device_num;
538             s_dss_env.evt_handler(&evt);
539         }
540     }
541 
542     if (rsp_id) {
543         dss_ctrl_pt_rsp_send(conn_idx, op_id, rsp_id);
544     }
545 }
546 
547 /**
548  *****************************************************************************************
549  * @brief Cancel Synchronization handler.
550  *
551  * @param[in] conn_idx: Connection index.
552  *****************************************************************************************
553  */
554 static void dss_op_cancel_sync_handler(uint8_t conn_idx)
555 {
556     dss_rsp_id_t rsp_id = DSS_RSP_ID_SUCCESS;
557     dss_evt_t    evt;
558 
559     if (((s_dss_env.sync_status != DSS_STATUS_IN_SCAN) && (DSS_ROLE_SYNC_SOURCE == s_dss_env.dss_role)) ||
560             ((s_dss_env.sync_status != DSS_STATUS_IN_ADV) && (DSS_ROLE_SYNC_DEVICE == s_dss_env.dss_role))) {
561         rsp_id = DSS_RSP_ID_STATUS_ERR;
562     } else if (s_dss_env.evt_handler == NULL) {
563         rsp_id = DSS_RSP_ID_NO_HANDLER;
564     } else {
565         evt.conn_idx = conn_idx;
566         evt.evt_type = DSS_EVT_SYNC_CANCEL;
567         s_dss_env.evt_handler(&evt);
568     }
569 
570     if (rsp_id) {
571         dss_ctrl_pt_rsp_send(conn_idx, DSS_OP_ID_CANCEL_SYNC, rsp_id);
572     }
573 }
574 
575 /**
576  *****************************************************************************************
577  * @brief Enter low power handler.
578  *
579  * @param[in] conn_idx: Connection index.
580  *****************************************************************************************
581  */
582 static void dss_op_lp_enter_handler(uint8_t conn_idx)
583 {
584     dss_rsp_id_t rsp_id = DSS_RSP_ID_SUCCESS;
585     dss_evt_t    evt;
586 
587     if (s_dss_env.sync_status != DSS_STATUS_CFG_READY) {
588         rsp_id = DSS_RSP_ID_STATUS_ERR;
589     } else if (s_dss_env.evt_handler == NULL) {
590         rsp_id = DSS_RSP_ID_NO_HANDLER;
591     } else {
592         s_dss_env.is_auto_enter_lp = true;
593         s_dss_env.sync_status      = DSS_STATUS_CFG_READY;
594 
595         evt.conn_idx               = conn_idx;
596         evt.evt_type               = DSS_EVT_LP_ENTER;
597         evt.is_enter_lp_mode       = s_dss_env.is_auto_enter_lp;
598 
599         dss_ctrl_pt_rsp_send(conn_idx, DSS_OP_ID_LP_ENTER, rsp_id);
600         s_dss_env.evt_handler(&evt);
601     }
602 
603     if (rsp_id) {
604         dss_ctrl_pt_rsp_send(conn_idx, DSS_OP_ID_LP_ENTER, rsp_id);
605     }
606 }
607 
608 /**
609  *****************************************************************************************
610  * @brief Destroy sync.
611  *
612  * @param[in] conn_idx: Connection index.
613  *****************************************************************************************
614  */
615 static void dss_op_sync_destroy_handler(uint8_t conn_idx)
616 {
617     dss_rsp_id_t rsp_id = DSS_RSP_ID_SUCCESS;
618     dss_evt_t    evt;
619 
620     if (s_dss_env.dss_role == DSS_ROLE_SYNC_INVALID) {
621         rsp_id = DSS_RSP_ID_ROLE_ERR;
622     } else if (ble_sync_source_destroy()) {
623         rsp_id = DSS_RSP_ID_DESTROY_SRC_FAIL;
624     } else {
625         s_dss_env.sync_status         = DSS_STATUS_CFG_READY;
626         s_dss_env.is_in_lp            = false;
627         s_dss_env.is_auto_enter_lp    = false;
628         s_dss_env.is_auto_calib_drift = false;
629         s_dss_env.event_period        = 0;
630         s_dss_env.sync_cnt            = 0;
631         s_dss_env.sync_device_num     = 0;
632 
633         evt.conn_idx                  = conn_idx;
634         evt.evt_type                  = DSS_EVT_SYNC_DESTROY;
635         s_dss_env.evt_handler(&evt);
636     }
637 
638     if (rsp_id) {
639         dss_ctrl_pt_rsp_send(conn_idx, DSS_OP_ID_SYNC_DESTROY, rsp_id);
640     }
641 }
642 
643 /**
644  *****************************************************************************************
645  * @brief Send Control Point Response.
646  *
647  * @param[in] conn_idx: Connection index.
648  * @param[in] op_id:    OP ID.
649  * @param[in] rsp_id:   Response id.
650  *****************************************************************************************
651  */
652 static sdk_err_t dss_ctrl_pt_rsp_send(uint8_t conn_idx, dss_op_id_t op_id, dss_rsp_id_t rsp_id)
653 {
654     sdk_err_t        error_code = SDK_ERR_IND_DISABLED;
655     uint8_t          rsp[DSS_CTRL_PT_RSP_VAL_LEN] = {0};
656     gatts_noti_ind_t rsp_ind;
657 
658     rsp[0] = DSS_OP_ID_RSP;
659     rsp[1] = op_id;
660     rsp[2] = rsp_id;
661 
662     if (s_dss_env.ctrl_pt_ind[conn_idx] == PRF_CLI_START_IND) {
663         rsp_ind.type   = BLE_GATT_INDICATION;
664         rsp_ind.handle = prf_find_handle_by_idx(DSS_IDX_CTRL_PT_VAL, s_dss_env.start_hdl,
665                                                 (uint8_t *)&s_dss_env.char_mask);
666         rsp_ind.length = DSS_CTRL_PT_RSP_VAL_LEN;
667         rsp_ind.value  = rsp;
668 
669         error_code = ble_gatts_noti_ind(conn_idx, &rsp_ind);
670     }
671 
672     return error_code;
673 }
674 
675 /**
676  *****************************************************************************************
677  * @brief Control Point handler.
678  *
679  * @param[in] conn_idx: Connection index.
680  * @param[in] p_data:   Pointer to data.
681  * @param[in] length:   Length of data.
682  *****************************************************************************************
683  */
684 static void dss_ctrl_pt_handler(uint8_t conn_idx, const uint8_t *p_data, uint16_t length)
685 {
686     switch (p_data[0]) {
687         case DSS_OP_ID_ROLE_SET:
688             dss_op_role_set_handler(conn_idx, p_data, length);
689             break;
690 
691         case DSS_OP_ID_SYNC_SRC_CREATE:
692             dss_op_sync_src_create_handler(conn_idx, p_data, length);
693             break;
694 
695         case DSS_OP_ID_SYNC:
696             dss_op_sync_handler(conn_idx, p_data, length, (dss_op_id_t)p_data[0]);
697             break;
698 
699         case DSS_OP_ID_LP_ENTER:
700             dss_op_lp_enter_handler(conn_idx);
701             break;
702 
703         case DSS_OP_ID_SYNC_DESTROY:
704             dss_op_sync_destroy_handler(conn_idx);
705             break;
706 
707         case DSS_OP_ID_CANCEL_SYNC:
708             dss_op_cancel_sync_handler(conn_idx);
709             break;
710 
711         default:
712             dss_ctrl_pt_rsp_send(conn_idx, (dss_op_id_t)p_data[0], DSS_RSP_ID_UNSUPPORT);
713             break;
714     }
715 }
716 
717 
718 static void dss_sync_evt_cb(uint32_t sync_cnt, uint16_t event_period)
719 {
720     gatts_noti_ind_t send_ntf;
721     sdk_err_t        error_code = SDK_ERR_NTF_DISABLED;
722     dss_evt_t        evt;
723 
724     if (s_dss_env.evt_handler == NULL) {
725         return;
726     }
727 
728     evt.evt_type = DSS_EVT_INVALID;
729 
730     evt.conn_idx = GAP_INVALID_CONN_INDEX;
731     evt.evt_type = DSS_EVT_SYNC_OCCUR;
732     evt.sync_cnt = sync_cnt;
733 
734     s_dss_env.evt_handler(&evt);
735 
736     if (!s_dss_env.is_in_lp && s_dss_env.is_auto_enter_lp && s_dss_env.evt_handler) {
737         evt.evt_type         = DSS_EVT_LP_ENTER;
738         evt.is_enter_lp_mode = true;
739 
740         s_dss_env.evt_handler(&evt);
741     }
742 
743     if (s_dss_env.is_auto_calib_drift && !(sync_cnt % s_dss_env.auto_calib_timing)) {
744         evt.sync_dev_num      = s_dss_env.sync_device_num;
745         evt.evt_type          = DSS_EVT_SYNC_SELF_OR_PEER;
746         evt.is_enter_lp_mode  = s_dss_env.is_auto_enter_lp;
747 
748         s_dss_env.sync_status = s_dss_env.dss_role == DSS_ROLE_SYNC_DEVICE ? DSS_STATUS_IN_ADV : DSS_STATUS_IN_SCAN;
749         s_dss_env.evt_handler(&evt);
750         s_dss_env.is_in_lp = false;
751     } else {
752         s_dss_env.sync_cnt     = sync_cnt;
753         s_dss_env.event_period = event_period;
754 
755         if (!s_dss_env.is_busy_send) {
756             if (s_dss_env.evt_cnt_ntf[s_dss_env.sync_cfg_conn_idx] == PRF_CLI_START_NTF) {
757                 send_ntf.type   = BLE_GATT_NOTIFICATION;
758                 send_ntf.handle = prf_find_handle_by_idx(DSS_IDX_SYNC_CNT_VAL, s_dss_env.start_hdl,
759                                                          (uint8_t *)&s_dss_env.char_mask);
760                 send_ntf.length = DSS_EVT_CNT_VALUE_LEN;
761                 send_ntf.value  = (uint8_t *)&s_dss_env.sync_cnt;
762 
763                 error_code = ble_gatts_noti_ind(s_dss_env.sync_cfg_conn_idx, &send_ntf);
764                 APP_ERROR_CHECK(error_code);
765             }
766         }
767     }
768 }
769 
770 /*
771  * GLOBAL FUNCTION DEFINITIONS
772  *****************************************************************************************
773  */
774 sdk_err_t dss_service_init(dss_evt_handler_t evt_handler)
775 {
776     if (evt_handler == NULL) {
777         return SDK_ERR_POINTER_NULL;
778     }
779 
780     ble_sync_evt_cb_register(dss_sync_evt_cb);
781 
782     s_dss_env.evt_handler         = evt_handler;
783     s_dss_env.dss_role            = DSS_ROLE_SYNC_INVALID;
784     s_dss_env.sync_status         = DSS_STATUS_CFG_READY;
785     s_dss_env.char_mask           = 0x1fff;
786     s_dss_env.is_busy_send        = false;
787     s_dss_env.is_auto_calib_drift = false;
788     s_dss_env.is_auto_enter_lp    = false;
789     s_dss_env.is_in_lp            = false;
790     s_dss_env.event_period        = 0;
791     s_dss_env.auto_calib_timing   = 0;
792     s_dss_env.sync_device_num     = 0;
793     s_dss_env.sync_cnt            = 0;
794 
795     return ble_server_prf_add(&dss_prf_info);
796 }
797 
798 sdk_err_t dss_sync_op_result_send(uint8_t conn_idx, dss_evt_type_t evt_type,  dss_rsp_id_t rsp_id)
799 {
800     if (evt_type == DSS_EVT_SYNC_SELF_OR_PEER) {
801         return dss_ctrl_pt_rsp_send(conn_idx, DSS_OP_ID_SYNC, rsp_id);
802     } else if (evt_type == DSS_EVT_SYNC_CANCEL) {
803         return dss_ctrl_pt_rsp_send(conn_idx, DSS_OP_ID_CANCEL_SYNC, rsp_id);
804     } else if (evt_type == DSS_EVT_LP_ENTER) {
805         return dss_ctrl_pt_rsp_send(conn_idx, DSS_OP_ID_LP_ENTER, rsp_id);
806     } else if (evt_type == DSS_EVT_SYNC_DESTROY) {
807         return dss_ctrl_pt_rsp_send(conn_idx, DSS_OP_ID_SYNC_DESTROY, rsp_id);
808     } else {
809         return SDK_ERR_INVALID_PARAM;
810     }
811 }
812 
813 
814 void dss_set_status(uint8_t conn_idx, dss_staus_t status)
815 {
816     s_dss_env.sync_status = status;
817 }
818 
819 void dss_set_sync_params(uint8_t conn_idx, bool is_auto_enter_lp, bool is_auto_calib_drift)
820 {
821     s_dss_env.is_auto_enter_lp    = is_auto_enter_lp;
822     s_dss_env.is_auto_calib_drift = is_auto_calib_drift;
823 }
824 
825 void dss_set_lp_mode(uint8_t conn_idx, bool is_in_lp_mode)
826 {
827     s_dss_env.is_in_lp = is_in_lp_mode;
828 }
829 
830 void dss_sync_src_distribute(uint8_t conn_idx)
831 {
832     dss_op_id_t  op_id;
833     dss_rsp_id_t rsp_id = DSS_RSP_ID_SUCCESS;
834 
835     if (DSS_ROLE_SYNC_SOURCE == s_dss_env.dss_role && ble_sync_source_distribute(conn_idx)) {
836         rsp_id = DSS_RSP_ID_DISTR_SRC_FAIL;
837     }
838 
839     op_id = DSS_OP_ID_SYNC;
840 
841     s_dss_env.sync_status = DSS_STATUS_CFG_READY;
842 
843     dss_ctrl_pt_rsp_send(s_dss_env.sync_cfg_conn_idx, op_id, rsp_id);
844 }
845