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