• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  ****************************************************************************************
3  *
4  * @file cts.c
5  *
6  * @brief Current Time 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 "cts.h"
43 #include "ble_prf_types.h"
44 #include "ble_prf_utils.h"
45 #include "utility.h"
46 #define INDEX_2 2
47 #define INDEX_3 3
48 #define INDEX_7 7
49 #define INDEX_8 8
50 #define INDEX_9 9
51 #define VALUE_7 7
52 #define VALUE_12 12
53 #define VALUE_31 31
54 #define VALUE_23 23
55 #define VALUE_59 59
56 /*
57  * ENUMERATIONS
58  ****************************************************************************************
59  */
60 /**@brief Current Time Service Attributes Indexes. */
61 enum {
62     // Current Time Service
63     CTS_IDX_SVC,
64 
65     // Current Time
66     CTS_IDX_CUR_TIME_CHAR,
67     CTS_IDX_CUR_TIME_VAL,
68     CTS_IDX_CUR_TIMR_NTF_CFG,
69 
70     // Local Time Information
71     CTS_IDX_LOC_TIME_INFO_CHAR,
72     CTS_IDX_LOC_TIME_INFO_VAL,
73 
74     // Reference Time Information
75     CTS_IDX_REF_TIME_INFO_CHAR,
76     CTS_IDX_REF_TIME_INFO_VAL,
77 
78     CTS_IDX_NB
79 };
80 
81 /*
82  * STRUCTURES
83  *****************************************************************************************
84  */
85 /**@brief Current Time Service environment variable. */
86 struct cts_env_t {
87     cts_init_t     cts_init;                              /**< Current Time Service initialization variables. */
88     uint16_t       start_hdl;                             /**< Current Time Service start handle. */
89     uint16_t
90     cur_time_ntf_cfg[CTS_CONNECTION_MAX];  /**< The configuration of Current Time Notification
91                                                 which is configured by the peer devices. */
92 };
93 
94 /*
95  * LOCAL FUNCTION DECLARATION
96  *****************************************************************************************
97  */
98 static sdk_err_t   cts_init(void);
99 static void        cts_read_att_cb(uint8_t conidx, const gatts_read_req_cb_t *p_param);
100 static void        cts_write_att_cb(uint8_t conidx, const gatts_write_req_cb_t *p_param);
101 static void        cts_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value);
102 static void        cts_gatts_ntf_ind_cb(uint8_t conn_idx, uint8_t status, const ble_gatts_ntf_ind_t *p_ntf_ind);
103 static void        cts_cur_time_read_handler(gatts_read_cfm_t *p_cfm, uint8_t *p_encode_buffer);
104 static void        cts_loc_time_info_read_handler(gatts_read_cfm_t *p_cfm, uint8_t *p_encode_buffer);
105 static void        cts_ref_time_info_read_handler(gatts_read_cfm_t *p_cfm, uint8_t *p_encode_buffer);
106 static void        cts_cur_time_write_handler(gatts_write_cfm_t *p_cfm, cts_evt_t *p_evt);
107 static void        cts_loc_time_info_write_handler(gatts_write_cfm_t *p_cfm, cts_evt_t *p_evt);
108 
109 /*
110  * LOCAL VARIABLE DEFINITIONS
111  *****************************************************************************************
112  */
113 static struct cts_env_t s_cts_env;
114 
115 /**@brief Full CTS Database Description - Used to add attributes into the database. */
116 static const attm_desc_t cts_attr_tab[CTS_IDX_NB] = {
117     // CTS Service Declaration
118     [CTS_IDX_SVC] = {BLE_ATT_DECL_PRIMARY_SERVICE, READ_PERM_UNSEC, 0, 0},
119 
120     // Current Time Characteristic Declaration
121     [CTS_IDX_CUR_TIME_CHAR]    = {BLE_ATT_DECL_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
122     // Current Time Characteristic Declaration value
123     [CTS_IDX_CUR_TIME_VAL]     = {
124         BLE_ATT_CHAR_CT_TIME,
125         READ_PERM_UNSEC | NOTIFY_PERM_UNSEC | WRITE_REQ_PERM_UNSEC,
126         ATT_VAL_LOC_USER,
127         CTS_CUR_TIME_VAL_LEN
128     },
129     // Current Time Characteristic Declaration  - Client Characteristic Configuration Descriptor
130     [CTS_IDX_CUR_TIMR_NTF_CFG] = {BLE_ATT_DESC_CLIENT_CHAR_CFG, READ_PERM_UNSEC | WRITE_REQ_PERM_UNSEC, 0, 0},
131 
132     // Local Time Information Characteristic Declaration
133     [CTS_IDX_LOC_TIME_INFO_CHAR] = {BLE_ATT_DECL_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
134     // Local Time Information Characteristic Value
135     [CTS_IDX_LOC_TIME_INFO_VAL]  = {
136         BLE_ATT_CHAR_LOCAL_TIME_INFO,
137         READ_PERM_UNSEC | WRITE_REQ_PERM_UNSEC,
138         ATT_VAL_LOC_USER,
139         CTS_LOC_TIME_INFO_VAL_LEN
140     },
141 
142     // Reference Time Information Characteristic Declaration
143     [CTS_IDX_REF_TIME_INFO_CHAR] = {BLE_ATT_DECL_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
144     // Reference Time Information Characteristic Value
145     [CTS_IDX_REF_TIME_INFO_VAL]  = {
146         BLE_ATT_CHAR_REFERENCE_TIME_INFO,
147         READ_PERM_UNSEC,
148         ATT_VAL_LOC_USER,
149         CTS_REF_TIME_INFO_VAL_LEN
150     },
151 };
152 
153 /**@brief CTS Task interface required by profile manager. */
154 static ble_prf_manager_cbs_t cts_tack_cbs = {
155     (prf_init_func_t) cts_init,
156     NULL,
157     NULL
158 };
159 
160 /**@brief CTS Task Callbacks. */
161 static gatts_prf_cbs_t cts_cb_func = {
162     cts_read_att_cb,
163     cts_write_att_cb,
164     NULL,
165     cts_gatts_ntf_ind_cb,
166     cts_cccd_set_cb
167 };
168 
169 /**@brief CTS Information. */
170 static const prf_server_info_t cts_prf_info = {
171     .max_connection_nb = CTS_CONNECTION_MAX,
172     .manager_cbs       = &cts_tack_cbs,
173     .gatts_prf_cbs     = &cts_cb_func
174 };
175 
176 /*
177  * LOCAL FUNCTION DEFINITIONS
178  *****************************************************************************************
179  */
180 /**
181  *****************************************************************************************
182  * @brief Initialize Current Time service and create db in att
183  *
184  * @return Error code to know if profile initialization succeed or not.
185  *****************************************************************************************
186  */
cts_init(void)187 static sdk_err_t cts_init(void)
188 {
189     // The start hanlde must be set with PRF_INVALID_HANDLE to be allocated automatically by BLE Stack.
190     uint16_t          start_hdl      = PRF_INVALID_HANDLE;
191     const uint8_t     cts_svc_uuid[] = BLE_ATT_16_TO_16_ARRAY(BLE_ATT_SVC_CURRENT_TIME);
192     sdk_err_t         error_code;
193     gatts_create_db_t gatts_db;
194 
195     error_code = memset_s(&gatts_db, sizeof(gatts_db), 0, sizeof(gatts_db));
196     if (error_code < 0) {
197         return error_code;
198     }
199 
200     gatts_db.shdl                 = &start_hdl;
201     gatts_db.uuid                 = cts_svc_uuid;
202     gatts_db.attr_tab_cfg         = (uint8_t *)&(s_cts_env.cts_init.char_mask);
203     gatts_db.max_nb_attr          = CTS_IDX_NB;
204     gatts_db.srvc_perm            = 0;
205     gatts_db.attr_tab_type        = SERVICE_TABLE_TYPE_16;
206     gatts_db.attr_tab.attr_tab_16 = cts_attr_tab;
207 
208     error_code = ble_gatts_srvc_db_create(&gatts_db);
209     if (SDK_SUCCESS == error_code) {
210         s_cts_env.start_hdl = *gatts_db.shdl;
211     }
212 
213     return error_code;
214 }
215 
216 /**
217  *****************************************************************************************
218  * @brief Handles reception of the attribute info request message.
219  *
220  * @param[in] conn_idx: Connection index
221  * @param[in] p_param:  The parameters of the read request.
222  *****************************************************************************************
223  */
cts_read_att_cb(uint8_t conn_idx,const gatts_read_req_cb_t * p_param)224 static void   cts_read_att_cb(uint8_t conn_idx, const gatts_read_req_cb_t *p_param)
225 {
226     gatts_read_cfm_t  cfm;
227     uint8_t           handle    = p_param->handle;
228     uint8_t           tab_index = prf_find_idx_by_handle(handle,
229                                   s_cts_env.start_hdl,
230                                   CTS_IDX_NB,
231                                   (uint8_t *)&s_cts_env.cts_init.char_mask);
232     cfm.handle = handle;
233     cfm.status = BLE_SUCCESS;
234 
235     switch (tab_index) {
236         case CTS_IDX_CUR_TIME_VAL: {
237             uint8_t encoded_buffer[CTS_CUR_TIME_VAL_LEN];
238             cts_cur_time_read_handler(&cfm, encoded_buffer);
239             break;
240         }
241 
242         case CTS_IDX_CUR_TIMR_NTF_CFG:
243             cfm.length = sizeof(uint16_t);
244             cfm.value  = (uint8_t *)&s_cts_env.cur_time_ntf_cfg[conn_idx];
245             break;
246 
247         case CTS_IDX_LOC_TIME_INFO_VAL: {
248             uint8_t encoded_buffer[CTS_LOC_TIME_INFO_VAL_LEN];
249             cts_loc_time_info_read_handler(&cfm, encoded_buffer);
250             break;
251         }
252 
253         case CTS_IDX_REF_TIME_INFO_VAL: {
254             uint8_t encoded_buffer[CTS_REF_TIME_INFO_VAL_LEN];
255             cts_ref_time_info_read_handler(&cfm, encoded_buffer);
256             break;
257         }
258 
259         default:
260             cfm.length = 0;
261             cfm.status = BLE_ATT_ERR_INVALID_HANDLE;
262             break;
263     }
264 
265     ble_gatts_read_cfm(conn_idx, &cfm);
266 }
267 
268 /**
269  *****************************************************************************************
270  * @brief Handles reception of the write request.
271  *
272  * @param[in]: conn_idx: Connection index
273  * @param[in]: p_param:  The parameters of the write request.
274  *****************************************************************************************
275  */
cts_write_att_cb(uint8_t conn_idx,const gatts_write_req_cb_t * p_param)276 static void   cts_write_att_cb(uint8_t conn_idx, const gatts_write_req_cb_t *p_param)
277 {
278     uint16_t          handle      = p_param->handle;
279     uint16_t          tab_index   = 0;
280     uint16_t          cccd_value  = 0;
281     cts_evt_t         event;
282     gatts_write_cfm_t cfm;
283     uint8_t ret;
284 
285     tab_index  = prf_find_idx_by_handle(handle,
286                                         s_cts_env.start_hdl,
287                                         CTS_IDX_NB,
288                                         (uint8_t *)&s_cts_env.cts_init.char_mask);
289     cfm.handle     = handle;
290     cfm.status     = BLE_SUCCESS;
291     event.evt_type = CTS_EVT_INVALID;
292     event.conn_idx = conn_idx;
293 
294     switch (tab_index) {
295         case CTS_IDX_CUR_TIME_VAL:
296             event.evt_type = CTS_EVT_CUR_TIME_SET_BY_PEER;
297             event.p_data   = p_param->value;
298             event.length   = p_param->length;
299             cts_cur_time_write_handler(&cfm, &event);
300             ret = memcpy_s(&event.cur_time, sizeof(cts_cur_time_t),
301                            &s_cts_env.cts_init.cur_time, sizeof(cts_cur_time_t));
302             if (ret < 0) {
303                 return;
304             }
305             break;
306 
307         case CTS_IDX_CUR_TIMR_NTF_CFG:
308             cccd_value     = le16toh(&p_param->value[0]);
309             event.evt_type = ((PRF_CLI_START_NTF == cccd_value) ? \
310                               CTS_EVT_CUR_TIME_NOTIFICATION_ENABLED : \
311                               CTS_EVT_CUR_TIME_NOTIFICATION_DISABLED);
312             s_cts_env.cur_time_ntf_cfg[conn_idx] = cccd_value;
313             break;
314 
315         case CTS_IDX_LOC_TIME_INFO_VAL:
316             event.evt_type = CTS_EVT_LOC_TIME_INFO_SET_BY_PEER;
317             event.p_data   = p_param->value;
318             event.length   = p_param->length;
319             cts_loc_time_info_write_handler(&cfm, &event);
320             ret = memcpy_s(&event.loc_time_info, sizeof(cts_loc_time_info_t),
321                            &s_cts_env.cts_init.loc_time_info, sizeof(cts_loc_time_info_t));
322             if (ret < 0) {
323                 return;
324             }
325             break;
326 
327         default:
328             cfm.status = BLE_ATT_ERR_INVALID_HANDLE;
329             break;
330     }
331 
332     ble_gatts_write_cfm(conn_idx, &cfm);
333 
334     if (BLE_ATT_ERR_INVALID_HANDLE != cfm.status &&
335         CTS_EVT_INVALID != event.evt_type && s_cts_env.cts_init.evt_handler) {
336         s_cts_env.cts_init.evt_handler(&event);
337     }
338 }
339 
340 /**
341  *****************************************************************************************
342  * @brief Handles reception of the cccd recover request.
343  *
344  * @param[in]: conn_idx:   Connection index
345  * @param[in]: handle:     The handle of cccd attribute.
346  * @param[in]: cccd_value: The value of cccd attribute.
347  *****************************************************************************************
348  */
cts_cccd_set_cb(uint8_t conn_idx,uint16_t handle,uint16_t cccd_value)349 static void cts_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value)
350 {
351     uint16_t          tab_index   = 0;
352     cts_evt_t         event;
353 
354     if (!prf_is_cccd_value_valid(cccd_value)) {
355         return;
356     }
357 
358     tab_index  = prf_find_idx_by_handle(handle,
359                                         s_cts_env.start_hdl,
360                                         CTS_IDX_NB,
361                                         (uint8_t *)&s_cts_env.cts_init.char_mask);
362 
363     event.evt_type = CTS_EVT_INVALID;
364     event.conn_idx = conn_idx;
365 
366     switch (tab_index) {
367         case CTS_IDX_CUR_TIMR_NTF_CFG:
368             event.evt_type = ((PRF_CLI_START_NTF == cccd_value) ? \
369                               CTS_EVT_CUR_TIME_NOTIFICATION_ENABLED : \
370                               CTS_EVT_CUR_TIME_NOTIFICATION_DISABLED);
371             s_cts_env.cur_time_ntf_cfg[conn_idx] = cccd_value;
372             break;
373 
374         default:
375             break;
376     }
377 
378     if (CTS_EVT_INVALID != event.evt_type && s_cts_env.cts_init.evt_handler) {
379         s_cts_env.cts_init.evt_handler(&event);
380     }
381 }
382 
383 /**
384  *****************************************************************************************
385  * @brief Handles reception of the complete event.
386  *
387  * @param[in] conn_idx: Connection index.
388  * @param[in] p_param:  Pointer to the parameters of the complete event.
389  *****************************************************************************************
390  */
cts_gatts_ntf_ind_cb(uint8_t conn_idx,uint8_t status,const ble_gatts_ntf_ind_t * p_ntf_ind)391 static void cts_gatts_ntf_ind_cb(uint8_t conn_idx, uint8_t status, const ble_gatts_ntf_ind_t *p_ntf_ind)
392 {
393     if (BLE_GATT_NOTIFICATION == p_ntf_ind->type) {
394         s_cts_env.cts_init.cur_time.adjust_reason = CTS_AR_NO_CHANGE;
395     }
396 }
397 
398 /**
399  *****************************************************************************************
400  * @brief Encode a Current Time.
401  *
402  * @param[in]  p_cur_time:     Pointer to Current Time value to be encoded.
403  * @param[out] p_encoded_data: Pointer to encoded data will be written.
404  *****************************************************************************************
405  */
cts_cur_time_encode(const cts_cur_time_t * p_cur_time,uint8_t * p_encoded_data)406 static void cts_cur_time_encode(const cts_cur_time_t *p_cur_time, uint8_t *p_encoded_data)
407 {
408     prf_pack_date_time(p_encoded_data, &p_cur_time->day_date_time.date_time);
409 
410     p_encoded_data[INDEX_7] = p_cur_time->day_date_time.day_of_week;
411     p_encoded_data[INDEX_8] = p_cur_time->day_date_time.fractions_256;
412     p_encoded_data[INDEX_9] = p_cur_time->adjust_reason;
413 }
414 
415 /**
416  *****************************************************************************************
417  * @brief Decode for a Current Time.
418  *
419  * @param[in]  p_data: Pointer to data to be decoded.
420  * @param[out] p_cur_time:     Pointer to Current Time.
421  *****************************************************************************************
422  */
cts_cur_time_decode(const uint8_t * p_data,cts_cur_time_t * p_cur_time)423 static void cts_cur_time_decode(const uint8_t *p_data, cts_cur_time_t *p_cur_time)
424 {
425     prf_unpack_date_time(p_data, &p_cur_time->day_date_time.date_time);
426 
427     p_cur_time->day_date_time.day_of_week   = p_data[INDEX_7];
428     p_cur_time->day_date_time.fractions_256 = p_data[INDEX_8];
429     p_cur_time->adjust_reason               = p_data[INDEX_9];
430 }
431 
432 /**
433  *****************************************************************************************
434  * @brief Handle Current Time read event.
435  *
436  * @param[out] p_cfm:           Pointer to GATT read attribute result description.
437  * @param[out] p_encode_buffer: Pointer to encoded data will be written.
438  *****************************************************************************************
439  */
cts_cur_time_read_handler(gatts_read_cfm_t * p_cfm,uint8_t * p_encode_buffer)440 static void cts_cur_time_read_handler(gatts_read_cfm_t *p_cfm, uint8_t *p_encode_buffer)
441 {
442     s_cts_env.cts_init.cur_time.adjust_reason = CTS_AR_NO_CHANGE;
443 
444     cts_cur_time_encode(&s_cts_env.cts_init.cur_time, p_encode_buffer);
445 
446     p_cfm->length = CTS_CUR_TIME_VAL_LEN;
447     p_cfm->value  = p_encode_buffer;
448 }
449 
450 /**
451  *****************************************************************************************
452  * @brief Handle Local Time Information read event.
453  *
454  * @param[out] p_cfm:           Pointer to GATT read attribute result description.
455  * @param[out] p_encode_buffer: Pointer to encoded data will be written.
456  *****************************************************************************************
457  */
cts_loc_time_info_read_handler(gatts_read_cfm_t * p_cfm,uint8_t * p_encode_buffer)458 static void cts_loc_time_info_read_handler(gatts_read_cfm_t *p_cfm, uint8_t *p_encode_buffer)
459 {
460     p_encode_buffer[0] = s_cts_env.cts_init.loc_time_info.time_zone;
461     p_encode_buffer[1] = s_cts_env.cts_init.loc_time_info.dst_offset;
462 
463     p_cfm->value  = p_encode_buffer;
464     p_cfm->length = CTS_LOC_TIME_INFO_VAL_LEN;
465 }
466 
467 /**
468  *****************************************************************************************
469  * @brief Handle Reference Time Information read event.
470  *
471  * @param[out] p_cfm:           Pointer to GATT read attribute result description.
472  * @param[out] p_encode_buffer: Pointer to encoded data will be written.
473  *****************************************************************************************
474  */
cts_ref_time_info_read_handler(gatts_read_cfm_t * p_cfm,uint8_t * p_encode_buffer)475 static void cts_ref_time_info_read_handler(gatts_read_cfm_t *p_cfm, uint8_t *p_encode_buffer)
476 {
477     p_encode_buffer[0] = s_cts_env.cts_init.ref_time_info.source;
478     p_encode_buffer[1] = s_cts_env.cts_init.ref_time_info.accuracy;
479     p_encode_buffer[INDEX_2] = s_cts_env.cts_init.ref_time_info.days_since_update;
480     p_encode_buffer[INDEX_3] = s_cts_env.cts_init.ref_time_info.hours_since_update;
481 
482     p_cfm->value  = p_encode_buffer;
483     p_cfm->length = CTS_REF_TIME_INFO_VAL_LEN;
484 }
485 
486 /**
487  *****************************************************************************************
488  * @brief Handle Current Time write event.
489  *
490  * @param[out] p_cfm: Pointer to GATT write attribute result description.
491  * @param[in]  p_evt: Pointer to CTS event.
492  *****************************************************************************************
493  */
cts_cur_time_write_handler(gatts_write_cfm_t * p_cfm,cts_evt_t * p_evt)494 static void cts_cur_time_write_handler(gatts_write_cfm_t *p_cfm, cts_evt_t *p_evt)
495 {
496     cts_cur_time_t  cur_time_set = {0};
497 
498     cts_cur_time_decode(p_evt->p_data, &cur_time_set);
499 
500     if ((CTS_TIME_YEAR_VALID_VAL_MIN <= cur_time_set.day_date_time.date_time.year) && \
501             (CTS_TIME_YEAR_VALID_VAL_MIN >= cur_time_set.day_date_time.date_time.year)) {
502         s_cts_env.cts_init.cur_time.day_date_time.date_time.year = cur_time_set.day_date_time.date_time.year;
503     } else {
504         if (cur_time_set.day_date_time.date_time.year == 0) {
505             s_cts_env.cts_init.cur_time.day_date_time.date_time.year = cur_time_set.day_date_time.date_time.year;
506         } else {
507             p_cfm->status = CTS_ERROR_FIELDS_IGNORED;
508         }
509     }
510 
511     if (cur_time_set.day_date_time.date_time.month <= VALUE_12) {
512         s_cts_env.cts_init.cur_time.day_date_time.date_time.month = cur_time_set.day_date_time.date_time.month;
513     } else {
514         p_cfm->status = CTS_ERROR_FIELDS_IGNORED;
515     }
516 
517     if (cur_time_set.day_date_time.date_time.day <= VALUE_31) {
518         s_cts_env.cts_init.cur_time.day_date_time.date_time.day = cur_time_set.day_date_time.date_time.day;
519     } else {
520         p_cfm->status = CTS_ERROR_FIELDS_IGNORED;
521     }
522 
523     if (cur_time_set.day_date_time.date_time.hour <= VALUE_23) {
524         s_cts_env.cts_init.cur_time.day_date_time.date_time.hour = cur_time_set.day_date_time.date_time.hour;
525     } else {
526         p_cfm->status = CTS_ERROR_FIELDS_IGNORED;
527     }
528 
529     if (cur_time_set.day_date_time.date_time.min <= VALUE_23) {
530         s_cts_env.cts_init.cur_time.day_date_time.date_time.min = cur_time_set.day_date_time.date_time.min;
531     } else {
532         p_cfm->status = CTS_ERROR_FIELDS_IGNORED;
533     }
534 
535     if (cur_time_set.day_date_time.date_time.sec <= VALUE_23) {
536         s_cts_env.cts_init.cur_time.day_date_time.date_time.sec = cur_time_set.day_date_time.date_time.sec;
537     } else {
538         p_cfm->status = CTS_ERROR_FIELDS_IGNORED;
539     }
540 
541     if (cur_time_set.day_date_time.day_of_week <= VALUE_7) {
542         s_cts_env.cts_init.cur_time.day_date_time.day_of_week = cur_time_set.day_date_time.day_of_week;
543     } else {
544         p_cfm->status = CTS_ERROR_FIELDS_IGNORED;
545     }
546 
547     if (cur_time_set.adjust_reason <= 0x0f) {
548         s_cts_env.cts_init.cur_time.adjust_reason = cur_time_set.adjust_reason;
549     } else {
550         p_cfm->status = CTS_ERROR_FIELDS_IGNORED;
551     }
552 }
553 
554 /**
555  *****************************************************************************************
556  * @brief Handle Local Time Information write event.
557  *
558  * @param[out] p_cfm: Pointer to GATT write attribute result description.
559  * @param[in]  p_evt: Pointer to CTS event.
560  *****************************************************************************************
561  */
cts_loc_time_info_write_handler(gatts_write_cfm_t * p_cfm,cts_evt_t * p_evt)562 static void cts_loc_time_info_write_handler(gatts_write_cfm_t *p_cfm, cts_evt_t *p_evt)
563 {
564     cts_dst_offset_t dst_offset = (cts_dst_offset_t)p_evt->p_data[1];
565 
566     if (CTS_TIME_ZONE_OFFSET_MIN <= (int8_t)p_evt->p_data[0] && CTS_TIME_ZONE_OFFSET_MAX >= (int8_t)p_evt->p_data[0]) {
567         s_cts_env.cts_init.loc_time_info.time_zone  = p_evt->p_data[0];
568     } else {
569         p_cfm->status = CTS_ERROR_FIELDS_IGNORED;
570     }
571 
572     if (CTS_DST_OFFSET_DOUB_DAYLIGHT_TIME >= dst_offset) {
573         s_cts_env.cts_init.loc_time_info.dst_offset = dst_offset;
574     } else {
575         p_cfm->status = CTS_ERROR_FIELDS_IGNORED;
576     }
577 
578     s_cts_env.cts_init.loc_time_info.dst_offset = (cts_dst_offset_t)p_evt->p_data[1];
579 }
580 
581 /*
582  * GLOBAL FUNCTION DEFINITIONS
583  *****************************************************************************************
584  */
cts_cur_time_send(uint8_t conn_idx,cts_cur_time_t * p_cur_time)585 sdk_err_t cts_cur_time_send(uint8_t conn_idx, cts_cur_time_t *p_cur_time)
586 {
587     sdk_err_t        error_code = SDK_ERR_NTF_DISABLED;
588     uint8_t          encoded_cur_time[CTS_CUR_TIME_VAL_LEN];
589     gatts_noti_ind_t ct_ntf;
590 
591     cts_cur_time_encode(p_cur_time, encoded_cur_time);
592 
593     if (PRF_CLI_START_NTF == s_cts_env.cur_time_ntf_cfg[conn_idx]) {
594         ct_ntf.type   = BLE_GATT_NOTIFICATION;
595         ct_ntf.handle = prf_find_handle_by_idx(CTS_IDX_CUR_TIME_VAL,
596                                                s_cts_env.start_hdl,
597                                                (uint8_t *)&s_cts_env.cts_init.char_mask);
598         ct_ntf.length = CTS_CUR_TIME_VAL_LEN;
599         ct_ntf.value  = encoded_cur_time;
600         error_code    = ble_gatts_noti_ind(conn_idx, &ct_ntf);
601     }
602 
603     return error_code;
604 }
605 
cts_exact_time_get(cts_exact_time_256_t * p_cts_exact_time)606 void cts_exact_time_get(cts_exact_time_256_t *p_cts_exact_time)
607 {
608     uint8_t ret;
609     ret = memcpy_s(p_cts_exact_time, sizeof(cts_exact_time_256_t),
610                    &s_cts_env.cts_init.cur_time.day_date_time, sizeof(cts_exact_time_256_t));
611     if (ret < 0) {
612         return;
613     }
614 }
615 
cts_exact_time_update(cts_exact_time_256_t * p_cts_exact_time)616 void cts_exact_time_update(cts_exact_time_256_t *p_cts_exact_time)
617 {
618     uint8_t ret;
619     ret = memcpy_s(&s_cts_env.cts_init.cur_time.day_date_time, sizeof(cts_exact_time_256_t),
620                    p_cts_exact_time, sizeof(cts_exact_time_256_t));
621     if (ret < 0) {
622         return;
623     }
624 }
625 
cts_cur_time_adjust(cts_adj_info_t * p_adj_info)626 void cts_cur_time_adjust(cts_adj_info_t *p_adj_info)
627 {
628     uint8_t ret;
629     if (CTS_AR_MAUAL_TIME_UPDATE & p_adj_info->adjust_reason) {
630         ret = memcpy_s(&s_cts_env.cts_init.cur_time.day_date_time,
631                        &p_adj_info->day_date_time, sizeof(cts_exact_time_256_t));
632         if (ret < 0) {
633             return;
634         }
635     }
636 
637     if (CTS_AR_EXT_REF_TIME_UPDATE & p_adj_info->adjust_reason) {
638         ret = memcpy_s(&s_cts_env.cts_init.ref_time_info,
639                        &p_adj_info->ref_time_info, sizeof(cts_ref_time_info_t));
640         if (ret < 0) {
641             return;
642         }
643     }
644 
645     if (CTS_AR_TIME_ZONE_CHANGE & p_adj_info->adjust_reason) {
646         s_cts_env.cts_init.loc_time_info.time_zone = p_adj_info->loc_time_info.time_zone;
647     }
648 
649     if (CTS_AR_DST_CHANGE & p_adj_info->adjust_reason) {
650         s_cts_env.cts_init.loc_time_info.dst_offset = p_adj_info->loc_time_info.dst_offset;
651     }
652 
653     s_cts_env.cts_init.cur_time.adjust_reason = p_adj_info->adjust_reason;
654 
655     cts_cur_time_send(0, &s_cts_env.cts_init.cur_time);
656 }
657 
cts_service_init(cts_init_t * p_cts_init)658 sdk_err_t cts_service_init(cts_init_t *p_cts_init)
659 {
660     uint8_t ret;
661     if (p_cts_init == NULL) {
662         return SDK_ERR_POINTER_NULL;
663     }
664 
665     ret = memset_s(&s_cts_env, sizeof(s_cts_env), 0, sizeof(s_cts_env));
666     if (ret < 0) {
667         return;
668     }
669     ret = memcpy_s(&s_cts_env.cts_init, sizeof(cts_init_t), p_cts_init, sizeof(cts_init_t));
670     if (ret < 0) {
671         return;
672     }
673     return ble_server_prf_add(&cts_prf_info);
674 }
675