1 /**
2 ****************************************************************************************
3 *
4 * @file hrrcps.c
5 *
6 * @brief HRS RSCS Relay Control Point 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 "hrrcps.h"
43 #include "ble_prf_types.h"
44 #include "ble_prf_utils.h"
45 #include "utility.h"
46
47 /*
48 * DEFINES
49 *****************************************************************************************
50 */
51 #define HRRCPS_CTRL_PT_CHARACTERISTIC_UUID {0x1B, 0xD7, 0x90, 0xEC, 0xE8, 0xB9, 0x75, 0x80, \
52 0x0A, 0x46, 0x44, 0xD3, 0x02, 0x06, 0xED, 0xA6}
53 #define HRRCPS_CTRL_PT_RSP_CHARACTERISTIC_UUID {0x1B, 0xD7, 0x90, 0xEC, 0xE8, 0xB9, 0x75, 0x80, \
54 0x0A, 0x46, 0x44, 0xD3, 0x03, 0x06, 0xED, 0xA6}
55
56 /**@brief Macros for conversion of 128bit to 16bit UUID. */
57 #define ATT_128_PRIMARY_SERVICE BLE_ATT_16_TO_128_ARRAY(BLE_ATT_DECL_PRIMARY_SERVICE)
58 #define ATT_128_CHARACTERISTIC BLE_ATT_16_TO_128_ARRAY(BLE_ATT_DECL_CHARACTERISTIC)
59 #define ATT_128_CLIENT_CHAR_CFG BLE_ATT_16_TO_128_ARRAY(BLE_ATT_DESC_CLIENT_CHAR_CFG)
60
61 /*
62 * ENUMERATIONS
63 ****************************************************************************************
64 */
65 /**@brief HRS RSCS Relay Control Point Service Attributes Indexes. */
66 enum {
67 // HRS RSCS Relay Control Point Service
68 HRRCPS_IDX_SVC,
69
70 // HRR Control Point
71 HRRCPS_IDX_HRR_CTRL_PT_CHAR,
72 HRRCPS_IDX_HRR_CTRL_PT_VAL,
73
74 // HRR Control Point Response
75 HRRCPS_IDX_HRR_CTRL_PT_RSP_CHAR,
76 HRRCPS_IDX_HRR_CTRL_PT_RSP_VAL,
77 HRRCPS_IDX_HRR_CTRL_PT_RSP_CFG,
78
79 HRRCPS_IDX_NB
80 };
81
82 /*
83 * STRUCTURES
84 *****************************************************************************************
85 */
86 /**@brief HRS RSCS Relay Control Point Service environment variable. */
87 struct hrrcps_env_t {
88 hrrcps_evt_handler_t
89 evt_handler; /**< HRS RSCS Relay Control Point Service event handler. */
90 uint16_t
91 start_hdl; /**< HRS RSCS Relay Control Point Service start handle. */
92 uint16_t
93 ctrl_pt_rsp_ind_cfg[HRRCPS_CONNECTION_MAX]; /**< The configuration of Control Point Response \
94 which is configured by the peer devices. */
95 };
96
97 /*
98 * LOCAL FUNCTION DECLARATION
99 *****************************************************************************************
100 */
101 static sdk_err_t hrrcps_init(void);
102 static void hrrcps_read_att_cb(uint8_t conn_idx, const gatts_read_req_cb_t *p_param);
103 static void hrrcps_write_att_cb(uint8_t conn_idx, const gatts_write_req_cb_t *p_param);
104 static void hrrcps_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value);
105 static void hrrcps_ctrl_pt_handler(uint8_t conn_idx, hrrcps_ctrl_pt_id_t ctrl_pt_id);
106
107 /*
108 * LOCAL VARIABLE DEFINITIONS
109 *****************************************************************************************
110 */
111 static struct hrrcps_env_t s_hrrcps_env;
112 static uint16_t s_hrrcps_char_mask = 0x7f;
113
114 /**@brief Full HRRCPS Database Description - Used to add attributes into the database. */
115 static const attm_desc_128_t hrrcps_attr_tab[HRRCPS_IDX_NB] = {
116 // HRS RSCS Relay Control Point Service
117 [HRRCPS_IDX_SVC] = {ATT_128_PRIMARY_SERVICE, READ_PERM_UNSEC, 0, 0},
118
119 // HRR Control Point Characteristic - Declaration
120 [HRRCPS_IDX_HRR_CTRL_PT_CHAR] = {ATT_128_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
121 // HRR Control Point Characteristic - Value
122 [HRRCPS_IDX_HRR_CTRL_PT_VAL] = {
123 HRRCPS_CTRL_PT_CHARACTERISTIC_UUID,
124 WRITE_REQ_PERM_UNSEC,
125 (ATT_VAL_LOC_USER | ATT_UUID_TYPE_SET(UUID_TYPE_128)),
126 HRRCPS_CTRL_PT_VAL_LEN
127 },
128
129 // HRR Control Point Response Characteristic - Declaration
130 [HRRCPS_IDX_HRR_CTRL_PT_RSP_CHAR] = {ATT_128_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
131 // HRR Control Point Response Characteristic - Value
132 [HRRCPS_IDX_HRR_CTRL_PT_RSP_VAL] = {
133 HRRCPS_CTRL_PT_RSP_CHARACTERISTIC_UUID,
134 INDICATE_PERM_UNSEC,
135 (ATT_VAL_LOC_USER | ATT_UUID_TYPE_SET(UUID_TYPE_128)),
136 HRRCPS_CTRL_PT_RSP_VAL_LEN
137 },
138 // HRR Control Point Responset Characteristic - Client Characteristic Configuration Descriptor
139 [HRRCPS_IDX_HRR_CTRL_PT_RSP_CFG] = {ATT_128_CLIENT_CHAR_CFG, READ_PERM_UNSEC | WRITE_REQ_PERM_UNSEC, 0, 0},
140 };
141
142 /**@brief HRRCPS Service interface required by profile manager. */
143 static ble_prf_manager_cbs_t hrrcps_mgr_cbs = {
144 (prf_init_func_t)hrrcps_init,
145 NULL,
146 NULL
147 };
148
149 /**@brief HRRCPS GATT Server Callbacks. */
150 static gatts_prf_cbs_t hrrcps_gatts_cbs = {
151 hrrcps_read_att_cb,
152 hrrcps_write_att_cb,
153 NULL,
154 NULL,
155 hrrcps_cccd_set_cb
156 };
157
158 /**@brief HRRCPS Service Information. */
159 static const prf_server_info_t hrrcps_prf_info = {
160 .max_connection_nb = HRRCPS_CONNECTION_MAX,
161 .manager_cbs = &hrrcps_mgr_cbs,
162 .gatts_prf_cbs = &hrrcps_gatts_cbs
163 };
164
165 /*
166 * LOCAL FUNCTION DEFINITIONS
167 *****************************************************************************************
168 */
169 /**
170 *****************************************************************************************
171 * @brief Initialize HRS RSCS Relay Control Point service and create database in ATT.
172 *
173 * @return Error code to know if profile initialization succeed or not.
174 *****************************************************************************************
175 */
hrrcps_init(void)176 static sdk_err_t hrrcps_init(void)
177 {
178 const uint8_t hrrcps_svc_uuid[] = {HRRCPS_SERVICE_UUID};
179 uint16_t start_hdl = PRF_INVALID_HANDLE;
180 sdk_err_t error_code;
181 gatts_create_db_t gatts_db;
182
183 error_code = memset_s(&gatts_db, sizeof(gatts_db), 0, sizeof(gatts_db));
184 if (error_code < 0) {
185 return error_code;
186 }
187
188 gatts_db.shdl = &start_hdl;
189 gatts_db.uuid = hrrcps_svc_uuid;
190 gatts_db.attr_tab_cfg = (uint8_t *)&s_hrrcps_char_mask;
191 gatts_db.max_nb_attr = HRRCPS_IDX_NB;
192 gatts_db.srvc_perm = SRVC_UUID_TYPE_SET(UUID_TYPE_128);
193 gatts_db.attr_tab_type = SERVICE_TABLE_TYPE_128;
194 gatts_db.attr_tab.attr_tab_128 = hrrcps_attr_tab;
195
196 error_code = ble_gatts_srvc_db_create(&gatts_db);
197 if (SDK_SUCCESS == error_code) {
198 s_hrrcps_env.start_hdl = *gatts_db.shdl;
199 }
200
201 return error_code;
202 }
203
204 /**
205 *****************************************************************************************
206 * @brief Handles reception of the attribute info request message.
207 *
208 * @param[in] conn_idx: Connection index.
209 * @param[in] p_param: Pointer to the parameters of the read request.
210 *****************************************************************************************
211 */
hrrcps_read_att_cb(uint8_t conn_idx,const gatts_read_req_cb_t * p_param)212 static void hrrcps_read_att_cb(uint8_t conn_idx, const gatts_read_req_cb_t *p_param)
213 {
214 gatts_read_cfm_t cfm;
215 uint8_t handle = p_param->handle;
216 uint8_t tab_index = 0;
217
218 tab_index = prf_find_idx_by_handle(handle, s_hrrcps_env.start_hdl, HRRCPS_IDX_NB, (uint8_t *)&s_hrrcps_char_mask);
219 cfm.handle = handle;
220 cfm.status = BLE_SUCCESS;
221
222 switch (tab_index) {
223 case HRRCPS_IDX_HRR_CTRL_PT_RSP_CFG:
224 cfm.length = sizeof(uint16_t);
225 cfm.value = (uint8_t *)&s_hrrcps_env.ctrl_pt_rsp_ind_cfg[conn_idx];
226 break;
227
228 default:
229 cfm.length = 0;
230 cfm.status = BLE_ATT_ERR_INVALID_HANDLE;
231 break;
232 }
233
234 ble_gatts_read_cfm(conn_idx, &cfm);
235 }
236
237 /**
238 *****************************************************************************************
239 * @brief Handles reception of the write request.
240 *
241 * @param[in] conn_idx: Connection index.
242 * @param[in] p_param: Pointer to the parameters of the write request.
243 *****************************************************************************************
244 */
hrrcps_write_att_cb(uint8_t conn_idx,const gatts_write_req_cb_t * p_param)245 static void hrrcps_write_att_cb(uint8_t conn_idx, const gatts_write_req_cb_t *p_param)
246 {
247 uint8_t handle = p_param->handle;
248 uint8_t tab_index = 0;
249 uint16_t cccd_value = 0;
250 bool is_ctrl_pt_valid = false;
251 hrrcps_evt_t event;
252 gatts_write_cfm_t cfm;
253
254 tab_index = prf_find_idx_by_handle(handle, s_hrrcps_env.start_hdl, HRRCPS_IDX_NB, (uint8_t *)&s_hrrcps_char_mask);
255 cfm.handle = handle;
256 cfm.status = BLE_SUCCESS;
257
258 switch (tab_index) {
259 case HRRCPS_IDX_HRR_CTRL_PT_VAL:
260 if ((HRRCPS_CTRL_PT_SCAN_HRS > p_param->value[0]) || \
261 (HRRCPS_CTRL_PT_RSCS_DISCONN < p_param->value[0])) {
262 cfm.status = 0x80;
263 } else {
264 is_ctrl_pt_valid = true;
265 }
266 break;
267
268 case HRRCPS_IDX_HRR_CTRL_PT_RSP_CFG:
269 cccd_value = le16toh(&p_param->value[0]);
270 event.evt_type = ((PRF_CLI_START_IND == cccd_value) ? \
271 HRRCPS_EVT_CTRL_PT_IND_ENABLE : \
272 HRRCPS_EVT_CTRL_PT_IND_DISABLE);
273 s_hrrcps_env.ctrl_pt_rsp_ind_cfg[conn_idx] = cccd_value;
274 break;
275
276 default:
277 cfm.status = BLE_ATT_ERR_INVALID_HANDLE;
278 break;
279 }
280
281 if (is_ctrl_pt_valid) {
282 hrrcps_ctrl_pt_handler(conn_idx, (hrrcps_ctrl_pt_id_t)p_param->value[0]);
283 } else if (BLE_ATT_ERR_INVALID_HANDLE != cfm.status && HRRCPS_EVT_INVALID != event.evt_type
284 && s_hrrcps_env.evt_handler) {
285 event.conn_idx = conn_idx;
286 s_hrrcps_env.evt_handler(&event);
287 }
288
289 ble_gatts_write_cfm(conn_idx, &cfm);
290 }
291
292 /**
293 *****************************************************************************************
294 * @brief Handle HRR Control Point.
295 *
296 * @param[in] conn_idx: Connection index.
297 * @param[in] ctrl_pt_id: HRRCPS Control Point ID
298 *
299 * @return True or False
300 *****************************************************************************************
301 */
hrrcps_ctrl_pt_handler(uint8_t conn_idx,hrrcps_ctrl_pt_id_t ctrl_pt_id)302 static void hrrcps_ctrl_pt_handler(uint8_t conn_idx, hrrcps_ctrl_pt_id_t ctrl_pt_id)
303 {
304 hrrcps_evt_t event;
305
306 event.conn_idx = conn_idx;
307
308 switch (ctrl_pt_id) {
309 case HRRCPS_CTRL_PT_SCAN_HRS:
310 event.evt_type = HRRCPS_EVT_SCAN_HRS;
311 break;
312
313 case HRRCPS_CTRL_PT_SCAN_RSCS:
314 event.evt_type = HRRCPS_EVT_SCAN_RSCS;
315 break;
316
317 case HRRCPS_CTRL_PT_HRS_SEN_LOC_READ:
318 event.evt_type = HRRCPS_EVT_HRS_SENSOR_LOC_READ;
319 break;
320
321 case HRRCPS_CTRL_PT_RSCS_SEN_LOC_READ:
322 event.evt_type = HRRCPS_EVT_RSCS_SENSOR_LOC_READ;
323 break;
324
325 case HRRCPS_CTRL_PT_HRS_NTF_ENABLE:
326 event.evt_type = HRRCPS_EVT_ENABLE_HRS_NTF;
327 break;
328
329 case HRRCPS_CTRL_PT_RSCS_NTF_ENABLE:
330 event.evt_type = HRRCPS_EVT_ENABLE_RSCS_NTF;
331 break;
332
333 case HRRCPS_CTRL_PT_HRS_NTF_DISABLE:
334 event.evt_type = HRRCPS_EVT_DISABLE_HRS_NTF;
335 break;
336
337 case HRRCPS_CTRL_PT_RSCS_NTF_DISABLE:
338 event.evt_type = HRRCPS_EVT_DISABLE_RSCS_NTF;
339 break;
340
341 case HRRCPS_CTRL_PT_HRS_DISCONN:
342 event.evt_type = HRRCPS_EVT_DISCONN_HRS_LINK;
343 break;
344
345 case HRRCPS_CTRL_PT_RSCS_DISCONN:
346 event.evt_type = HRRCPS_EVT_DISCONN_RSCS_LINK;
347 break;
348
349 default:
350 break;
351 }
352
353 if (HRRCPS_EVT_INVALID != event.evt_type && s_hrrcps_env.evt_handler) {
354 s_hrrcps_env.evt_handler(&event);
355 }
356 }
357
358 /**
359 *****************************************************************************************
360 * @brief Handles reception of the cccd recover request.
361 *
362 * @param[in]: conn_idx: Connection index
363 * @param[in]: handle: The handle of cccd attribute.
364 * @param[in]: cccd_value: The value of cccd attribute.
365 *****************************************************************************************
366 */
hrrcps_cccd_set_cb(uint8_t conn_idx,uint16_t handle,uint16_t cccd_value)367 static void hrrcps_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value)
368 {
369 uint8_t tab_index = 0;
370 hrrcps_evt_t event;
371
372 if (!prf_is_cccd_value_valid(cccd_value)) {
373 return;
374 }
375
376 tab_index = prf_find_idx_by_handle(handle, s_hrrcps_env.start_hdl, HRRCPS_IDX_NB, (uint8_t *)&s_hrrcps_char_mask);
377
378 switch (tab_index) {
379 case HRRCPS_IDX_HRR_CTRL_PT_RSP_CFG:
380 event.evt_type = ((PRF_CLI_START_IND == cccd_value) ? \
381 HRRCPS_EVT_CTRL_PT_IND_ENABLE : \
382 HRRCPS_EVT_CTRL_PT_IND_DISABLE);
383 s_hrrcps_env.ctrl_pt_rsp_ind_cfg[conn_idx] = cccd_value;
384 break;
385
386 default:
387 event.evt_type = HRRCPS_EVT_INVALID;
388 break;
389 }
390
391 if (HRRCPS_EVT_INVALID != event.evt_type && s_hrrcps_env.evt_handler) {
392 event.conn_idx = conn_idx;
393 s_hrrcps_env.evt_handler(&event);
394 }
395 }
396
397 /**
398 *****************************************************************************************
399 * @brief Encode HRR Control Point value.
400 *
401 * @param[in] p_ctrl_val_pt: Pointer to Control Point value.
402 * @param[out] p_encoded_buff: Pointer to buffer encoded.
403 *
404 * @return Length of encoded
405 *****************************************************************************************
406 */
hrrcps_ctrl_pt_rsp_encode(hrrcps_rsp_val_t * p_rsp_val,uint8_t * p_encoded_buff)407 static uint16_t hrrcps_ctrl_pt_rsp_encode(hrrcps_rsp_val_t *p_rsp_val, uint8_t *p_encoded_buff)
408 {
409 uint16_t length = 0;
410
411 p_encoded_buff[length++] = HRRCPS_CTRL_PT_RSP_CODE;
412 p_encoded_buff[length++] = p_rsp_val->cmd_id;
413 p_encoded_buff[length++] = p_rsp_val->rsp_id;
414
415 if (p_rsp_val->is_inc_prama) {
416 p_encoded_buff[length++] = p_rsp_val->rsp_param;
417 }
418
419 return length;
420 }
421
422 /*
423 * GLOBAL FUNCTION DEFINITIONS
424 *****************************************************************************************
425 */
hrrcps_ctrl_pt_rsp_send(uint8_t conn_idx,hrrcps_rsp_val_t * p_rsp_val)426 sdk_err_t hrrcps_ctrl_pt_rsp_send(uint8_t conn_idx, hrrcps_rsp_val_t *p_rsp_val)
427 {
428 uint8_t encoded_ctrl_pt_rsp[HRRCPS_CTRL_PT_RSP_VAL_LEN];
429 gatts_noti_ind_t ctrl_pt_rsp_ind;
430 uint16_t encoded_length;
431
432 encoded_length =hrrcps_ctrl_pt_rsp_encode(p_rsp_val, encoded_ctrl_pt_rsp);
433
434 if (PRF_CLI_START_IND == s_hrrcps_env.ctrl_pt_rsp_ind_cfg[conn_idx]) {
435 ctrl_pt_rsp_ind.type = BLE_GATT_INDICATION;
436 ctrl_pt_rsp_ind.handle = prf_find_handle_by_idx(HRRCPS_IDX_HRR_CTRL_PT_RSP_VAL,
437 s_hrrcps_env.start_hdl,
438 (uint8_t *)&s_hrrcps_char_mask);
439 ctrl_pt_rsp_ind.length = encoded_length;
440 ctrl_pt_rsp_ind.value = encoded_ctrl_pt_rsp;
441
442 return ble_gatts_noti_ind(conn_idx, &ctrl_pt_rsp_ind);
443 }
444
445 return SDK_ERR_IND_DISABLED;
446 }
447
hrrcps_service_init(hrrcps_evt_handler_t evt_handler)448 sdk_err_t hrrcps_service_init(hrrcps_evt_handler_t evt_handler)
449 {
450 s_hrrcps_env.evt_handler = evt_handler;
451 return ble_server_prf_add(&hrrcps_prf_info);
452 }
453