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