• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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