1 /**
2 *****************************************************************************************
3 *
4 * @file cscs.c
5 *
6 * @brief Cycling Speed and Cadence Profile Sensor 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 "cscs.h"
43 #include "ble_prf_types.h"
44 #include "ble_prf_utils.h"
45 #include "utility.h"
46
47 /*
48 * ENUMERATIONS
49 *****************************************************************************************
50 */
51 /**@brief Cycling Speed and Cadence Service Attributes Indexes. */
52 enum {
53 CSCS_IDX_SVC,
54
55 CSCS_IDX_CSC_MEAS_CHAR,
56 CSCS_IDX_CSC_MEAS_VAL,
57 CSCS_IDX_CSC_MEAS_NTF_CFG,
58
59 CSCS_IDX_CSC_FEAT_CHAR,
60 CSCS_IDX_CSC_FEAT_VAL,
61
62 CSCS_IDX_SENSOR_LOC_CHAR,
63 CSCS_IDX_SENSOR_LOC_VAL,
64
65 CSCS_IDX_CTRL_POINT_CHAR,
66 CSCS_IDX_CTRL_POINT_VAL,
67 CSCS_IDX_CTRL_POINT_IND_CFG,
68
69 CSCS_IDX_NB
70 };
71
72 /*
73 * STRUCTURES
74 *****************************************************************************************
75 */
76 /**@brief Cycling Speed and Cadence Service environment variable. */
77 struct cscs_env_t {
78 cscs_init_t
79 cscs_init; /**< Cycling Speed and Cadence Service initialization variables. */
80 uint16_t start_hdl; /**< Cycling Speed and Cadence Service start handle. */
81 bool
82 ctrl_pt_op_in_progress; /**< A previously triggered SC Control Point operation is still in progress. */
83 bool
84 ctrl_pt_op_rsp_cplt; /**< A previously triggered SC Control Point operation response cplt. */
85 uint16_t
86 meas_ntf_cfg[CSCS_CONNECTION_MAX]; /**< The configuration of CSC Measurement Notification \
87 which is configured by the peer devices. */
88 uint16_t
89 ctrl_point_ind_cfg[CSCS_CONNECTION_MAX]; /**< The configuration of SC Control Point Notification \
90 which is configured by the peer devices. */
91 };
92
93 /*
94 * LOCAL FUNCTION DECLARATION
95 *****************************************************************************************
96 */
97 static sdk_err_t cscs_init(void);
98 static void cscs_read_att_cb(uint8_t conn_idx, const gatts_read_req_cb_t *p_param);
99 static void cscs_write_att_cb(uint8_t conn_idx, const gatts_write_req_cb_t *p_param);
100 static void cscs_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value);
101 static void cscs_disconnect_cb(uint8_t conn_idx, uint8_t reason);
102 static void cscs_ntf_ind_cb(uint8_t conn_idx, uint8_t status, const ble_gatts_ntf_ind_t *p_ntf_ind);
103 static void cscs_sc_ctrl_pt_handler(uint8_t conn_idx, const uint8_t *p_data, uint16_t length);
104
105 /*
106 * LOCAL VARIABLE DEFINITIONS
107 *****************************************************************************************
108 */
109 static struct cscs_env_t s_cscs_env;
110
111 /**@brief Full CSCS Database Description - Used to add attributes into the database. */
112 static const attm_desc_t cscs_attr_tab[CSCS_IDX_NB] = {
113 // Cycling Speed and Cadence Service Declaration
114 [CSCS_IDX_SVC] = {BLE_ATT_DECL_PRIMARY_SERVICE, READ_PERM_UNSEC, 0, 0},
115
116 // CSC Measurement Characteristic - Declaration
117 [CSCS_IDX_CSC_MEAS_CHAR] = {BLE_ATT_DECL_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
118 // CSC Measurement Characteristic - Value
119 [CSCS_IDX_CSC_MEAS_VAL] = {
120 BLE_ATT_CHAR_CSC_MEAS,
121 NOTIFY_PERM_UNSEC,
122 ATT_VAL_LOC_USER,
123 CSCS_MEAS_VAL_LEN_MAX
124 },
125 // CSC Measurement Characteristic - Client Characteristic Configuration Descriptor
126 [CSCS_IDX_CSC_MEAS_NTF_CFG] = {
127 BLE_ATT_DESC_CLIENT_CHAR_CFG,
128 READ_PERM_UNSEC | WRITE_REQ_PERM_UNSEC,
129 0,
130 0
131 },
132
133 // CS Feature Characteristic - Declaration
134 [CSCS_IDX_CSC_FEAT_CHAR] = {BLE_ATT_DECL_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
135 // CSC Feature Characteristic - Value
136 [CSCS_IDX_CSC_FEAT_VAL] = {
137 BLE_ATT_CHAR_CSC_FEAT,
138 READ_PERM_UNSEC,
139 ATT_VAL_LOC_USER,
140 CSCS_FEAT_VAL_LEN_MAX
141 },
142
143 // Sensor Location Characteristic - Declaration
144 [CSCS_IDX_SENSOR_LOC_CHAR] = {BLE_ATT_DECL_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
145 // Sensor Location Characteristic - Value
146 [CSCS_IDX_SENSOR_LOC_VAL] = {
147 BLE_ATT_CHAR_SENSOR_LOC,
148 READ_PERM_UNSEC,
149 ATT_VAL_LOC_USER,
150 CSCS_SENSOR_LOC_VAL_LEN_MAX
151 },
152
153 // SC Control Point Characteristic - Declaration
154 [CSCS_IDX_CTRL_POINT_CHAR] = {BLE_ATT_DECL_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
155 // SC Control Point Characteristic - Value
156 [CSCS_IDX_CTRL_POINT_VAL] = {
157 BLE_ATT_CHAR_SC_CNTL_PT,
158 WRITE_REQ_PERM_UNSEC | INDICATE_PERM_UNSEC,
159 ATT_VAL_LOC_USER,
160 CSCS_CTRL_PT_VAL_LEN_MAX
161 },
162 // SC Control Point Characteristic - Client Characteristic Configuration Descriptor
163 [CSCS_IDX_CTRL_POINT_IND_CFG] = {
164 BLE_ATT_DESC_CLIENT_CHAR_CFG,
165 READ_PERM_UNSEC | WRITE_REQ_PERM_UNSEC,
166 0,
167 0
168 },
169 };
170
171 /**@brief CSCS Task interface required by profile manager. */
172 static ble_prf_manager_cbs_t cscs_task_cbs = {
173 (prf_init_func_t) cscs_init,
174 NULL,
175 cscs_disconnect_cb
176 };
177
178 /**@brief CSCS Task Callbacks. */
179 static gatts_prf_cbs_t cscs_cb_func = {
180 cscs_read_att_cb,
181 cscs_write_att_cb,
182 NULL,
183 cscs_ntf_ind_cb,
184 cscs_cccd_set_cb
185 };
186
187 /**@brief CSCS Information. */
188 static const prf_server_info_t cscs_prf_info = {
189 .max_connection_nb = CSCS_CONNECTION_MAX,
190 .manager_cbs = &cscs_task_cbs,
191 .gatts_prf_cbs = &cscs_cb_func,
192 };
193
194 /*
195 * LOCAL FUNCTION DEFINITIONS
196 *****************************************************************************************
197 */
198 /**
199 *****************************************************************************************
200 * @brief Initialize Cycling Speed and Cadence service create db in att
201 *
202 * @return Error code to know if profile initialization succeed or not.
203 *****************************************************************************************
204 */
cscs_init(void)205 static sdk_err_t cscs_init(void)
206 {
207 // The start hanlde must be set with PRF_INVALID_HANDLE to be allocated automatically by BLE Stack.
208 uint16_t start_hdl = PRF_INVALID_HANDLE;
209 const uint8_t rscs_svc_uuid[] = BLE_ATT_16_TO_16_ARRAY(BLE_ATT_SVC_CYCLING_SPEED_CADENCE);
210 sdk_err_t error_code;
211 gatts_create_db_t gatts_db;
212
213 error_code = memset_s(&gatts_db, sizeof(gatts_db), 0, sizeof(gatts_db));
214 if (error_code < 0) {
215 return error_code;
216 }
217
218 gatts_db.shdl = &start_hdl;
219 gatts_db.uuid = rscs_svc_uuid;
220 gatts_db.attr_tab_cfg = (uint8_t *)&(s_cscs_env.cscs_init.char_mask);
221 gatts_db.max_nb_attr = CSCS_IDX_NB;
222 gatts_db.srvc_perm = 0;
223 gatts_db.attr_tab_type = SERVICE_TABLE_TYPE_16;
224 gatts_db.attr_tab.attr_tab_16 = cscs_attr_tab;
225
226 error_code = ble_gatts_srvc_db_create(&gatts_db);
227 if (SDK_SUCCESS == error_code) {
228 s_cscs_env.start_hdl = *gatts_db.shdl;
229 }
230
231 return error_code;
232 }
233
234 /**
235 *****************************************************************************************
236 * @brief Handles reception of the attribute info request message.
237 *
238 * @param[in] conn_idx: Connection index
239 * @param[in] p_param: The parameters of the read request.
240 *****************************************************************************************
241 */
cscs_read_att_cb(uint8_t conn_idx,const gatts_read_req_cb_t * p_param)242 static void cscs_read_att_cb(uint8_t conn_idx, const gatts_read_req_cb_t *p_param)
243 {
244 gatts_read_cfm_t cfm;
245 uint8_t handle = p_param->handle;
246 uint8_t tab_index = prf_find_idx_by_handle(handle,
247 s_cscs_env.start_hdl,
248 CSCS_IDX_NB,
249 (uint8_t *)&s_cscs_env.cscs_init.char_mask);
250 cfm.handle = handle;
251 cfm.status = BLE_SUCCESS;
252
253 switch (tab_index) {
254 case CSCS_IDX_CSC_MEAS_NTF_CFG:
255 cfm.length = sizeof(uint16_t);
256 cfm.value = (uint8_t *)&s_cscs_env.meas_ntf_cfg[conn_idx];
257 break;
258
259 case CSCS_IDX_CSC_FEAT_VAL:
260 cfm.length = sizeof(uint16_t);
261 cfm.value = (uint8_t *)&s_cscs_env.cscs_init.feature;
262 break;
263
264 case CSCS_IDX_SENSOR_LOC_VAL:
265 cfm.length = sizeof(uint8_t);
266 cfm.value = (uint8_t *)&s_cscs_env.cscs_init.sensor_location;
267 break;
268
269 case CSCS_IDX_CTRL_POINT_IND_CFG:
270 cfm.length = sizeof(uint16_t);
271 cfm.value = (uint8_t *)&s_cscs_env.ctrl_point_ind_cfg[conn_idx];
272 break;
273
274 default:
275 cfm.length = 0;
276 cfm.status = BLE_ATT_ERR_INVALID_HANDLE;
277 break;
278 }
279
280 ble_gatts_read_cfm(conn_idx, &cfm);
281 }
282
283 /**
284 *****************************************************************************************
285 * @brief Handles reception of the write request.
286 *
287 * @param[in]: conn_idx: Connection index
288 * @param[in]: p_param: The parameters of the write request.
289 *****************************************************************************************
290 */
cscs_write_att_cb(uint8_t conn_idx,const gatts_write_req_cb_t * p_param)291 static void cscs_write_att_cb(uint8_t conn_idx, const gatts_write_req_cb_t *p_param)
292 {
293 uint16_t handle = p_param->handle;
294 uint16_t tab_index = 0;
295 uint16_t cccd_value = 0;
296 bool ctrl_pt_evt = false;
297 cscs_evt_t event;
298 gatts_write_cfm_t cfm;
299
300 tab_index = prf_find_idx_by_handle(handle,
301 s_cscs_env.start_hdl,
302 CSCS_IDX_NB,
303 (uint8_t *)&s_cscs_env.cscs_init.char_mask);
304 cfm.handle = handle;
305 cfm.status = BLE_SUCCESS;
306 event.evt_type = CSCS_EVT_INVALID;
307 event.conn_idx = conn_idx;
308
309 switch (tab_index) {
310 case CSCS_IDX_CSC_MEAS_NTF_CFG:
311 cccd_value = le16toh(&p_param->value[0]);
312 event.evt_type = ((PRF_CLI_START_NTF == cccd_value) ?\
313 CSCS_EVT_CSC_MEAS_NOTIFICATION_ENABLE :\
314 CSCS_EVT_CSC_MEAS_NOTIFICATION_DISABLE);
315 s_cscs_env.meas_ntf_cfg[conn_idx] = cccd_value;
316 break;
317
318 case CSCS_IDX_CTRL_POINT_VAL:
319 if (PRF_CLI_START_IND != s_cscs_env.ctrl_point_ind_cfg[conn_idx]) {
320 cfm.status = CSCS_ERROR_CCCD_INVALID;
321 break;
322 } else if (s_cscs_env.ctrl_pt_op_in_progress) {
323 cfm.status = CSCS_ERROR_PROC_IN_PROGRESS;
324 } else if (PRF_CLI_START_IND == s_cscs_env.ctrl_point_ind_cfg[conn_idx]) {
325 s_cscs_env.ctrl_pt_op_in_progress = true;
326 ctrl_pt_evt = true;
327 }
328 break;
329
330 case CSCS_IDX_CTRL_POINT_IND_CFG:
331 cccd_value = le16toh(&p_param->value[0]);
332 event.evt_type = ((PRF_CLI_START_IND == cccd_value) ?\
333 CSCS_EVT_CTRL_POINT_INDICATION_ENABLE :\
334 CSCS_EVT_CTRL_POINT_INDICATION_DISABLE);
335 s_cscs_env.ctrl_point_ind_cfg[conn_idx] = cccd_value;
336 break;
337
338 default:
339 cfm.status = BLE_ATT_ERR_INVALID_HANDLE;
340 break;
341 }
342
343 ble_gatts_write_cfm(conn_idx, &cfm);
344
345 if (ctrl_pt_evt) {
346 cscs_sc_ctrl_pt_handler(conn_idx, p_param->value, p_param->length);
347 } else if (BLE_ATT_ERR_INVALID_HANDLE != cfm.status && CSCS_EVT_INVALID != event.evt_type
348 && s_cscs_env.cscs_init.evt_handler) {
349 s_cscs_env.cscs_init.evt_handler(&event);
350 }
351 }
352
353 /**
354 *****************************************************************************************
355 * @brief Handles reception of the cccd recover request.
356 *
357 * @param[in]: conn_idx: Connection index
358 * @param[in]: handle: The handle of cccd attribute.
359 * @param[in]: cccd_value: The value of cccd attribute.
360 *****************************************************************************************
361 */
cscs_cccd_set_cb(uint8_t conn_idx,uint16_t handle,uint16_t cccd_value)362 static void cscs_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value)
363 {
364 uint16_t tab_index = 0;
365 cscs_evt_t event;
366
367 if (!prf_is_cccd_value_valid(cccd_value)) {
368 return;
369 }
370
371 tab_index = prf_find_idx_by_handle(handle,
372 s_cscs_env.start_hdl,
373 CSCS_IDX_NB,
374 (uint8_t *)&s_cscs_env.cscs_init.char_mask);
375
376 event.evt_type = CSCS_EVT_INVALID;
377 event.conn_idx = conn_idx;
378
379 switch (tab_index) {
380 case CSCS_IDX_CSC_MEAS_NTF_CFG:
381 event.evt_type = ((PRF_CLI_START_NTF == cccd_value) ?\
382 CSCS_EVT_CSC_MEAS_NOTIFICATION_ENABLE :\
383 CSCS_EVT_CSC_MEAS_NOTIFICATION_DISABLE);
384 s_cscs_env.meas_ntf_cfg[conn_idx] = cccd_value;
385 break;
386
387 case CSCS_IDX_CTRL_POINT_IND_CFG:
388 event.evt_type = ((PRF_CLI_START_IND == cccd_value) ?\
389 CSCS_EVT_CTRL_POINT_INDICATION_ENABLE :\
390 CSCS_EVT_CTRL_POINT_INDICATION_DISABLE);
391 s_cscs_env.ctrl_point_ind_cfg[conn_idx] = cccd_value;
392 break;
393
394 default:
395 break;
396 }
397
398 if (CSCS_EVT_INVALID != event.evt_type && s_cscs_env.cscs_init.evt_handler) {
399 s_cscs_env.cscs_init.evt_handler(&event);
400 }
401 }
402
403 /**
404 *****************************************************************************************
405 * @brief Handles reception of the complete event.
406 *
407 * @param[in] conn_idx: Connection index.
408 * @param[in] p_param: Pointer to the parameters of the complete event.
409 *****************************************************************************************
410 */
cscs_ntf_ind_cb(uint8_t conn_idx,uint8_t status,const ble_gatts_ntf_ind_t * p_ntf_ind)411 static void cscs_ntf_ind_cb(uint8_t conn_idx, uint8_t status, const ble_gatts_ntf_ind_t *p_ntf_ind)
412 {
413 cscs_evt_t event;
414
415 event.evt_type = CSCS_EVT_INVALID;
416 event.conn_idx = conn_idx;
417
418 if (s_cscs_env.cscs_init.evt_handler && SDK_SUCCESS == status) {
419 if (BLE_GATT_NOTIFICATION == p_ntf_ind->type) {
420 event.evt_type = CSCS_EVT_CSC_MEAS_SEND_CPLT;
421 } else if (BLE_GATT_INDICATION == p_ntf_ind->type) {
422 event.evt_type = CSCS_EVT_CTRL_POINT_RSP_CPLT;
423 s_cscs_env.ctrl_pt_op_in_progress = false;
424 }
425 s_cscs_env.cscs_init.evt_handler(&event);
426 }
427 }
428
429 /**
430 *****************************************************************************************
431 * @brief Handles reception of the disconnection event.
432 *
433 * @param[in] conn_idx: Connection index.
434 * @param[in] reason: Reason of disconnection.
435 *****************************************************************************************
436 */
cscs_disconnect_cb(uint8_t conn_idx,uint8_t reason)437 static void cscs_disconnect_cb(uint8_t conn_idx, uint8_t reason)
438 {
439 s_cscs_env.ctrl_pt_op_in_progress = false;
440 }
441
442 /**
443 *****************************************************************************************
444 * @brief SC Control Point Set Cumulative value handler.
445 *
446 * @param[in] conn_idx: Connection index.
447 * @param[in] p_data: Pointer to data.
448 * @param[in] length: Length of data.
449 *****************************************************************************************
450 */
cscs_op_set_cumulative_handler(uint8_t conn_idx,const uint8_t * p_data,uint16_t length)451 static void cscs_op_set_cumulative_handler(uint8_t conn_idx, const uint8_t *p_data, uint16_t length)
452 {
453 cscs_evt_t event;
454 uint8_t rsp[CSCS_CTRL_PT_RSP_LEN_MIN];
455
456 rsp[INDEX_0] = CSCS_CTRL_PT_OP_RSP_CODE;
457 rsp[INDEX_1] = CSCS_CTRL_PT_OP_SET_CUMUL_VAL;
458 ATrsp[INDEX_2] = CSCS_CTRL_PT_RSP_FAILED;
459
460 if ((sizeof(uint32_t) == length) && \
461 (s_cscs_env.cscs_init.feature & CSCS_FEAT_WHEEL_REVOLUTION_SUP_BIT) && \
462 (s_cscs_env.cscs_init.evt_handler)) {
463 event.conn_idx = conn_idx;
464 event.evt_type = CSCS_EVT_CUMUL_VAL_SET;
465 event.p_data = p_data;
466 event.length = length;
467 s_cscs_env.cscs_init.evt_handler(&event);
468 } else {
469 cscs_ctrl_pt_rsp_send(conn_idx, rsp, CSCS_CTRL_PT_RSP_LEN_MIN);
470 }
471 }
472
473 /**
474 *****************************************************************************************
475 * @brief SC Control Point Sensor Location update handler.
476 *
477 * @param[in] conn_idx: Connection index.
478 * @param[in] p_data: Pointer to data.
479 * @param[in] length: Length of data.
480 *****************************************************************************************
481 */
cscs_op_sensor_loc_update_handler(uint8_t conn_idx,const uint8_t * p_data,uint16_t length)482 static void cscs_op_sensor_loc_update_handler(uint8_t conn_idx, const uint8_t *p_data, uint16_t length)
483 {
484 cscs_evt_t event;
485 uint8_t rsp[CSCS_CTRL_PT_RSP_LEN_MIN];
486
487 rsp[INDEX_0] = CSCS_CTRL_PT_OP_RSP_CODE;
488 rsp[INDEX_1] = CSCS_CTRL_PT_OP_UPD_LOC;
489
490 if (CSCS_SENSOR_LOC_SUP_NB <= p_data[0] || (sizeof(uint8_t) != length)) {
491 rsp[INDEX_2] = CSCS_CTRL_PT_RSP_INVALID_PARAM;
492 cscs_ctrl_pt_rsp_send(conn_idx, rsp, CSCS_CTRL_PT_RSP_LEN_MIN);
493 } else if ((s_cscs_env.cscs_init.feature & CSCS_FEAT_MULTIPLE_SENSORS_BIT) && s_cscs_env.cscs_init.evt_handler) {
494 event.conn_idx = conn_idx;
495 event.evt_type = CSCS_EVT_SEBSOR_LOC_UPD;
496 event.p_data = p_data;
497 event.length = length;
498 s_cscs_env.cscs_init.evt_handler(&event);
499 } else {
500 rsp[INDEX_2] = CSCS_CTRL_PT_RSP_FAILED;
501 cscs_ctrl_pt_rsp_send(conn_idx, rsp, CSCS_CTRL_PT_RSP_LEN_MIN);
502 }
503 }
504
505 /**
506 *****************************************************************************************
507 * @brief SC Control Point Suppoted Sensor Location list request handler.
508 *
509 * @param[in] conn_idx: Connection index.
510 *****************************************************************************************
511 */
cscs_op_sup_sensor_loc_req_handler(uint8_t conn_idx)512 static void cscs_op_sup_sensor_loc_req_handler(uint8_t conn_idx)
513 {
514 cscs_evt_t event;
515 uint8_t rsp[CSCS_CTRL_PT_RSP_LEN_MIN + CSCS_SENSOR_LOC_SUP_NB];
516 uint8_t rsp_idx = CSCS_CTRL_PT_RSP_LEN_MIN;
517
518 rsp[INDEX_0] = CSCS_CTRL_PT_OP_RSP_CODE;
519 rsp[INDEX_1] = CSCS_CTRL_PT_OP_REQ_SUP_LOC;
520 rsp[INDEX_2] = CSCS_CTRL_PT_RSP_SUCCESS;
521
522 if (s_cscs_env.cscs_init.feature & CSCS_FEAT_MULTIPLE_SENSORS_BIT) {
523 event.conn_idx = conn_idx;
524 event.evt_type = CSCS_EVT_SUP_SEBSOR_LOC_REQ;
525
526 if (s_cscs_env.cscs_init.evt_handler) {
527 s_cscs_env.cscs_init.evt_handler(&event);
528 }
529
530 for (uint8_t i = 0; i < CSCS_SENSOR_LOC_SUP_NB; i++) {
531 rsp[rsp_idx++] = i;
532 }
533 cscs_ctrl_pt_rsp_send(conn_idx, rsp, CSCS_CTRL_PT_RSP_LEN_MIN + CSCS_SENSOR_LOC_SUP_NB);
534 } else {
535 rsp[INDEX_2] = CSCS_CTRL_PT_RSP_FAILED;
536 cscs_ctrl_pt_rsp_send(conn_idx, rsp, CSCS_CTRL_PT_RSP_LEN_MIN);
537 }
538 }
539
540 /**
541 *****************************************************************************************
542 * @brief SC Control Point handler.
543 *
544 * @param[in] conn_idx: Connection index.
545 * @param[in] p_data: Pointer to data.
546 * @param[in] length: Length of data.
547 *****************************************************************************************
548 */
cscs_sc_ctrl_pt_handler(uint8_t conn_idx,const uint8_t * p_data,uint16_t length)549 static void cscs_sc_ctrl_pt_handler(uint8_t conn_idx, const uint8_t *p_data, uint16_t length)
550 {
551 uint8_t rsp[CSCS_CTRL_PT_RSP_LEN_MIN];
552
553 switch (p_data[0]) {
554 case CSCS_CTRL_PT_OP_SET_CUMUL_VAL:
555 cscs_op_set_cumulative_handler(conn_idx, &p_data[1], length - 1);
556 break;
557
558 case CSCS_CTRL_PT_OP_START_CALIB:
559 rsp[INDEX_0] = CSCS_CTRL_PT_OP_RSP_CODE;
560 rsp[INDEX_1] = p_data[0];
561 rsp[INDEX_2] = CSCS_CTRL_PT_RSP_NOT_SUP;
562 cscs_ctrl_pt_rsp_send(conn_idx, rsp, CSCS_CTRL_PT_RSP_LEN_MIN);
563 break;
564
565 case CSCS_CTRL_PT_OP_UPD_LOC:
566 cscs_op_sensor_loc_update_handler(conn_idx, &p_data[1], length - 1);
567 break;
568
569 case CSCS_CTRL_PT_OP_REQ_SUP_LOC:
570 cscs_op_sup_sensor_loc_req_handler(conn_idx);
571 break;
572
573 default:
574 rsp[INDEX_0] = CSCS_CTRL_PT_OP_RSP_CODE;
575 rsp[INDEX_1] = p_data[0];
576 rsp[INDEX_2] = CSCS_CTRL_PT_RSP_NOT_SUP;
577 cscs_ctrl_pt_rsp_send(conn_idx, rsp, CSCS_CTRL_PT_RSP_LEN_MIN);
578 break;
579 }
580 }
581
582 /**
583 *****************************************************************************************
584 * @brief Encoding a CSC Measurement.
585 *
586 * @param[in] p_meas: Pointer to CSC measurement value to be encoded.
587 * @param[out] p_encoded_buffer: Buffer where the encoded data will be written.
588 *
589 * @return Length of encoded data.
590 *****************************************************************************************
591 */
csc_meas_value_encoded(cscs_meas_val_t * p_meas,uint8_t * p_encoded_buffer)592 static uint16_t csc_meas_value_encoded(cscs_meas_val_t *p_meas, uint8_t *p_encoded_buffer)
593 {
594 uint8_t flags = 0;
595 uint16_t length = 1;
596
597 // Cumulative Wheel Revolutions and Last Wheel Event Time Fields
598 if (s_cscs_env.cscs_init.feature & CSCS_FEAT_WHEEL_REVOLUTION_SUP_BIT) {
599 if (p_meas->wheel_rev_data_present) {
600 p_encoded_buffer[length++] = LO_UINT32_T(p_meas->cumulative_wheel_revs);
601 p_encoded_buffer[length++] = L2_UINT32_T(p_meas->cumulative_wheel_revs);
602 p_encoded_buffer[length++] = L3_UINT32_T(p_meas->cumulative_wheel_revs);
603 p_encoded_buffer[length++] = HI_UINT32_T(p_meas->cumulative_wheel_revs);
604 p_encoded_buffer[length++] = LO_U16(p_meas->last_wheel_event_time);
605 p_encoded_buffer[length++] = HI_U16(p_meas->last_wheel_event_time);
606
607 // Flags field
608 flags |= CSCS_MEAS_FLAG_WHEEL_REVOLUTION_BIT;
609 }
610 }
611
612 // Cumulative Crank Revolutions and Last Crank Event Time Fields
613 if (s_cscs_env.cscs_init.feature & CSCS_FEAT_CRANK_REVOLUTION_SUP_BIT) {
614 if (p_meas->crank_rev_data_present) {
615 p_encoded_buffer[length++] = LO_U16(p_meas->cumulative_crank_revs);
616 p_encoded_buffer[length++] = HI_U16(p_meas->cumulative_crank_revs);
617 p_encoded_buffer[length++] = LO_U16(p_meas->last_crank_event_time);
618 p_encoded_buffer[length++] = HI_U16(p_meas->last_crank_event_time);
619
620 // Flags field
621 flags |= CSCS_MEAS_FLAG_CRANK_REVOLUTION_BIT;
622 }
623 }
624
625 p_encoded_buffer[0] = flags;
626
627 return length;
628 }
629
630 /*
631 * GLOBAL FUNCTION DEFINITIONS
632 ****************************************************************************************
633 */
cscs_measurement_send(uint8_t conn_idx,cscs_meas_val_t * p_meas)634 sdk_err_t cscs_measurement_send(uint8_t conn_idx, cscs_meas_val_t *p_meas)
635 {
636 sdk_err_t error_code = SDK_ERR_NTF_DISABLED;
637 uint8_t encoded_csc_meas[CSCS_MEAS_VAL_LEN_MAX];
638 uint16_t length;
639 gatts_noti_ind_t csc_ntf;
640
641 length = csc_meas_value_encoded(p_meas, encoded_csc_meas);
642
643 if (PRF_CLI_START_NTF == s_cscs_env.meas_ntf_cfg[conn_idx]) {
644 csc_ntf.type = BLE_GATT_NOTIFICATION;
645 csc_ntf.handle = prf_find_handle_by_idx(CSCS_IDX_CSC_MEAS_VAL,
646 s_cscs_env.start_hdl,
647 (uint8_t *)&s_cscs_env.cscs_init.char_mask);
648 csc_ntf.length = length;
649 csc_ntf.value = encoded_csc_meas;
650 error_code = ble_gatts_noti_ind(conn_idx, &csc_ntf);
651 }
652
653 return error_code;
654 }
655
cscs_ctrl_pt_rsp_send(uint8_t conn_idx,uint8_t * p_data,uint16_t length)656 sdk_err_t cscs_ctrl_pt_rsp_send(uint8_t conn_idx, uint8_t *p_data, uint16_t length)
657 {
658 sdk_err_t error_code = SDK_ERR_IND_DISABLED;
659 gatts_noti_ind_t ctrl_pt_rsp;
660
661 if (PRF_CLI_START_IND == s_cscs_env.ctrl_point_ind_cfg[conn_idx]) {
662 ctrl_pt_rsp.type = BLE_GATT_INDICATION;
663 ctrl_pt_rsp.handle = prf_find_handle_by_idx(CSCS_IDX_CTRL_POINT_VAL,
664 s_cscs_env.start_hdl,
665 (uint8_t *)&s_cscs_env.cscs_init.char_mask);
666 ctrl_pt_rsp.length = length;
667 ctrl_pt_rsp.value = p_data;
668 error_code = ble_gatts_noti_ind(conn_idx, &ctrl_pt_rsp);
669 }
670
671 return error_code;
672 }
673
cscs_sensor_loc_update(cscs_sensor_loc_t sensor_loc)674 sdk_err_t cscs_sensor_loc_update(cscs_sensor_loc_t sensor_loc)
675 {
676 sdk_err_t error_code = BLE_SUCCESS;
677
678 if (s_cscs_env.cscs_init.feature & CSCS_FEAT_MULTIPLE_SENSORS_BIT) {
679 s_cscs_env.cscs_init.sensor_location = sensor_loc;
680 } else {
681 error_code = SDK_ERR_DISALLOWED;
682 }
683
684 return error_code;
685 }
686
cscs_service_init(cscs_init_t * p_cscs_init)687 sdk_err_t cscs_service_init(cscs_init_t *p_cscs_init)
688 {
689 sdk_err_t ret;
690 if (p_cscs_init == NULL) {
691 return SDK_ERR_POINTER_NULL;
692 }
693
694 if (p_cscs_init->feature & CSCS_FEAT_MULTIPLE_SENSORS_BIT) {
695 p_cscs_init->char_mask |= CSCS_CHAR_SENSOR_LOC_SUP;
696 } else {
697 p_cscs_init->char_mask &= ~CSCS_CHAR_SENSOR_LOC_SUP;
698 }
699
700 ret = memcpy_s(&s_cscs_env.cscs_init, sizeof(cscs_init_t), p_cscs_init, sizeof(cscs_init_t));
701 if (ret < 0) {
702 return ret;
703 }
704
705 return ble_server_prf_add(&cscs_prf_info);
706 }
707