1 /**
2 *****************************************************************************************
3 *
4 * @file pcs.c
5 *
6 * @brief PCS 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 "pcs.h"
43 #include "ble_prf_types.h"
44 #include "ble_prf_utils.h"
45 #include "utility.h"
46
47 /*
48 * DEFINES
49 *****************************************************************************************
50 */
51 /**@brief The UUIDs of PCS characteristics. */
52 #define PCS_CHARACTERISTIC_TX_UUID {0x1B, 0xD7, 0x90, 0xEC, 0xE8, 0xB9, 0x75, 0x80, \
53 0x0A, 0x46, 0x44, 0xD3, 0x02, 0x05, 0xED, 0xA6}
54 #define PCS_CHARACTERISTIC_SETTING_UUID {0x1B, 0xD7, 0x90, 0xEC, 0xE8, 0xB9, 0x75, 0x80, \
55 0x0A, 0x46, 0x44, 0xD3, 0x03, 0x05, 0xED, 0xA6}
56
57 /**@brief Macros for conversion of 128bit to 16bit UUID. */
58 #define ATT_128_PRIMARY_SERVICE BLE_ATT_16_TO_128_ARRAY(BLE_ATT_DECL_PRIMARY_SERVICE)
59 #define ATT_128_CHARACTERISTIC BLE_ATT_16_TO_128_ARRAY(BLE_ATT_DECL_CHARACTERISTIC)
60 #define ATT_128_CLIENT_CHAR_CFG BLE_ATT_16_TO_128_ARRAY(BLE_ATT_DESC_CLIENT_CHAR_CFG)
61
62 /*
63 * ENUMERATIONS
64 *****************************************************************************************
65 */
66 /**@brief PCS Service Attributes Indexes. */
67 enum pcs_attr_idx_t {
68 PCS_IDX_SVC,
69
70 PCS_IDX_TX_CHAR,
71 PCS_IDX_TX_VAL,
72 PCS_IDX_TX_CFG,
73
74 PCS_IDX_SETTING_CHAR,
75 PCS_IDX_SETTING_VAL,
76 PCS_IDX_SETTING_CFG,
77
78 PCS_IDX_NB,
79 };
80
81 /*
82 * STRUCTURES
83 *****************************************************************************************
84 */
85 /**@brief PCS Service environment variable. */
86 struct pcs_env_t {
87 pcs_init_t pcs_init; /**< PCS Service initialization variables. */
88 uint16_t start_hdl; /**< Start handle of services */
89 uint16_t tx_ntf_cfg[PCS_CONNECTION_MAX]; /**< TX Characteristic Notification configuration of the peers. */
90 uint16_t setting_ind_cfg[PCS_CONNECTION_MAX]; /**< Setting Characteristic Indication configuration
91 of the peers. */
92 };
93
94 /*
95 * LOCAL FUNCTION DECLARATION
96 *****************************************************************************************
97 */
98 static sdk_err_t pcs_init(void);
99 static void pcs_disconnected(uint8_t conn_idx, uint8_t disconn_reason);
100 static void pcs_write_att_cb(uint8_t conn_idx, const gatts_write_req_cb_t *p_param);
101 static void pcs_read_att_cb(uint8_t conn_idx, const gatts_read_req_cb_t *p_param);
102 static void pcs_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value);
103 static void pcs_ntf_ind_cb(uint8_t conn_idx, uint8_t status, const ble_gatts_ntf_ind_t *p_ntf_ind);
104
105 /*
106 * LOCAL VARIABLE DEFINITIONS
107 *****************************************************************************************
108 */
109 static struct pcs_env_t s_pcs_env;
110 static const uint16_t s_char_mask = 0xFFFF;
111
112 /**@brief Full PCS Database Description which is used to add attributes into the ATT database. */
113 static const attm_desc_128_t pcs_att_db[PCS_IDX_NB] = {
114 // PCS service
115 [PCS_IDX_SVC] = {ATT_128_PRIMARY_SERVICE, READ_PERM_UNSEC, 0, 0},
116
117 // PCS TX Characteristic Declaration
118 [PCS_IDX_TX_CHAR] = {ATT_128_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
119 // PCS TX Characteristic Value
120 [PCS_IDX_TX_VAL] = {
121 PCS_CHARACTERISTIC_TX_UUID,
122 NOTIFY_PERM_UNSEC,
123 (ATT_VAL_LOC_USER | ATT_UUID_TYPE_SET(UUID_TYPE_128)),
124 PCS_MAX_DATA_LEN
125 },
126 // PCS TX Characteristic - Client Characteristic Configuration Descriptor
127 [PCS_IDX_TX_CFG] = {
128 ATT_128_CLIENT_CHAR_CFG,
129 READ_PERM_UNSEC | WRITE_REQ_PERM_UNSEC,
130 0,
131 0
132 },
133
134 // PCS settings
135 [PCS_IDX_SETTING_CHAR] = {ATT_128_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
136 // PCS settings Value
137 [PCS_IDX_SETTING_VAL] = {
138 PCS_CHARACTERISTIC_SETTING_UUID,
139 (WRITE_CMD_PERM_UNSEC | INDICATE_PERM_UNSEC),
140 (ATT_VAL_LOC_USER | ATT_UUID_TYPE_SET(UUID_TYPE_128)),
141 PCS_MAX_DATA_LEN
142 },
143 // ths settings cfg
144 [PCS_IDX_SETTING_CFG] = {
145 ATT_128_CLIENT_CHAR_CFG,
146 (READ_PERM_UNSEC | WRITE_REQ_PERM_UNSEC | WRITE_CMD_PERM_UNSEC),
147 0,
148 0
149 },
150 };
151
152 /**@brief PCS Service interface required by profile manager. */
153 static ble_prf_manager_cbs_t pcs_mgr_cbs = {
154 (prf_init_func_t)pcs_init,
155 NULL,
156 pcs_disconnected,
157 };
158
159 /**@brief PCS GATT Server Callbacks. */
160 static gatts_prf_cbs_t pcs_gatts_cbs = {
161 pcs_read_att_cb,
162 pcs_write_att_cb,
163 NULL,
164 pcs_ntf_ind_cb,
165 pcs_cccd_set_cb
166 };
167
168 /**@brief PCS Server Information. */
169 static const prf_server_info_t pcs_prf_info = {
170 .max_connection_nb = PCS_CONNECTION_MAX,
171 .manager_cbs = &pcs_mgr_cbs,
172 .gatts_prf_cbs = &pcs_gatts_cbs
173 };
174
175 /*
176 * LOCAL FUNCTION DEFINITIONS
177 *****************************************************************************************
178 */
179 /**
180 *****************************************************************************************
181 * @brief Initialize PCS and create DB in ATT.
182 *
183 * @return Error code to know if service initialization succeed or not.
184 *****************************************************************************************
185 */
pcs_init(void)186 static sdk_err_t pcs_init(void)
187 {
188 const uint8_t pcs_svc_uuid[] = {PCS_SERVICE_UUID};
189 uint16_t start_hdl = PRF_INVALID_HANDLE;
190 sdk_err_t error_code;
191 gatts_create_db_t gatts_db;
192
193 error_code = memset_s(&gatts_db, sizeof(gatts_db), 0, sizeof(gatts_db));
194 if (error_code < 0) {
195 return error_code;
196 }
197
198 gatts_db.shdl = &start_hdl;
199 gatts_db.uuid = pcs_svc_uuid;
200 gatts_db.attr_tab_cfg = (uint8_t *)&s_char_mask;
201 gatts_db.max_nb_attr = PCS_IDX_NB;
202 gatts_db.srvc_perm = SRVC_UUID_TYPE_SET(UUID_TYPE_128);
203 gatts_db.attr_tab_type = SERVICE_TABLE_TYPE_128;
204 gatts_db.attr_tab.attr_tab_128 = pcs_att_db;
205
206 error_code = ble_gatts_srvc_db_create(&gatts_db);
207 if (SDK_SUCCESS == error_code) {
208 s_pcs_env.start_hdl = *gatts_db.shdl;
209 }
210
211 return error_code;
212 }
213
214 /**
215 *****************************************************************************************
216 * @brief Handles reception of the attribute info request message.
217 *
218 * @param[in] conn_idx: Index of the connection.
219 * @param[in] p_param: Pointer to the parameters of the read request.
220 *****************************************************************************************
221 */
pcs_read_att_cb(uint8_t conn_idx,const gatts_read_req_cb_t * p_param)222 static void pcs_read_att_cb(uint8_t conn_idx, const gatts_read_req_cb_t *p_param)
223 {
224 gatts_read_cfm_t cfm;
225 uint16_t handle = p_param->handle;
226 uint8_t tab_index = 0;
227
228 tab_index = prf_find_idx_by_handle(handle, s_pcs_env.start_hdl, PCS_IDX_NB, (uint8_t *)&s_char_mask);
229 cfm.handle = handle;
230 cfm.status = BLE_SUCCESS;
231
232 switch (tab_index) {
233 case PCS_IDX_TX_CFG:
234 cfm.length = sizeof(uint16_t);
235 cfm.value = (uint8_t *)&s_pcs_env.tx_ntf_cfg[conn_idx];
236 cfm.status = BLE_SUCCESS;
237 break;
238
239 case PCS_IDX_SETTING_CFG:
240 cfm.length = sizeof(uint16_t);
241 cfm.value = (uint8_t *)&s_pcs_env.setting_ind_cfg[conn_idx];
242 cfm.status = BLE_SUCCESS;
243 break;
244
245 default:
246 cfm.length = 0;
247 cfm.status = BLE_ATT_ERR_INVALID_HANDLE;
248 break;
249 }
250
251 ble_gatts_read_cfm(conn_idx, &cfm);
252 }
253
254 /**
255 *****************************************************************************************
256 * @brief Handles reception of the write request.
257 *
258 * @param[in] conn_idx: Index of the connection.
259 * @param[in] p_param: Point to the parameters of the write request.
260 *****************************************************************************************
261 */
pcs_write_att_cb(uint8_t conn_idx,const gatts_write_req_cb_t * p_param)262 static void pcs_write_att_cb(uint8_t conn_idx, const gatts_write_req_cb_t *p_param)
263 {
264 uint8_t handle = p_param->handle;
265 uint8_t tab_index = 0;
266 uint16_t cccd_value;
267 pcs_evt_t event;
268 gatts_write_cfm_t cfm;
269
270 tab_index = prf_find_idx_by_handle(handle, s_pcs_env.start_hdl, PCS_IDX_NB, (uint8_t *)&s_char_mask);
271 event.conn_idx = conn_idx;
272 cfm.handle = handle;
273 cfm.status = BLE_SUCCESS;
274
275 switch (tab_index) {
276 case PCS_IDX_TX_CFG:
277 cccd_value = le16toh(&p_param->value[0]);
278 event.evt_type = (PRF_CLI_START_NTF == cccd_value) ? PCS_EVT_TX_ENABLE : PCS_EVT_TX_DISABLE;
279 s_pcs_env.tx_ntf_cfg[conn_idx] = cccd_value;
280 break;
281
282 case PCS_IDX_SETTING_VAL:
283 event.evt_type = PCS_EVT_PARAM_SET;
284 break;
285
286 case PCS_IDX_SETTING_CFG:
287 cccd_value = le16toh(&p_param->value[0]);
288 event.evt_type = (PRF_CLI_START_IND == cccd_value) ? PCS_EVT_SETTING_ENABLE : PCS_EVT_SETTING_DISABLE;
289 s_pcs_env.setting_ind_cfg[conn_idx] = cccd_value;
290 break;
291
292 default:
293 cfm.status = BLE_ATT_ERR_INVALID_HANDLE;
294 break;
295 }
296
297 if (BLE_ATT_ERR_INVALID_HANDLE != cfm.status && PCS_EVT_INVALID != event.evt_type &&
298 s_pcs_env.pcs_init.evt_handler) {
299 event.conn_idx = conn_idx;
300 event.p_data = (uint8_t *)p_param->value;
301 event.length = p_param->length;
302
303 s_pcs_env.pcs_init.evt_handler(&event);
304 }
305
306 ble_gatts_write_cfm(conn_idx, &cfm);
307 }
308
309 /**
310 *****************************************************************************************
311 * @brief Handles reception of the cccd recover request.
312 *
313 * @param[in]: conn_idx: Connection index
314 * @param[in]: handle: The handle of cccd attribute.
315 * @param[in]: cccd_value: The value of cccd attribute.
316 *****************************************************************************************
317 */
pcs_cccd_set_cb(uint8_t conn_idx,uint16_t handle,uint16_t cccd_value)318 static void pcs_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value)
319 {
320 uint8_t tab_index = 0;
321 pcs_evt_t event;
322
323 if (!prf_is_cccd_value_valid(cccd_value)) {
324 return;
325 }
326
327 tab_index = prf_find_idx_by_handle(handle, s_pcs_env.start_hdl, PCS_IDX_NB, (uint8_t *)&s_char_mask);
328 event.conn_idx = conn_idx;
329 event.evt_type = PCS_EVT_INVALID;
330
331 switch (tab_index) {
332 case PCS_IDX_SETTING_CFG:
333 event.evt_type = (PRF_CLI_START_IND == cccd_value) ? PCS_EVT_SETTING_ENABLE : PCS_EVT_SETTING_DISABLE;
334 s_pcs_env.setting_ind_cfg[conn_idx] = cccd_value;
335 break;
336
337 default:
338 break;
339 }
340
341 if (PCS_EVT_INVALID != event.evt_type && s_pcs_env.pcs_init.evt_handler) {
342 s_pcs_env.pcs_init.evt_handler(&event);
343 }
344 }
345
346 /**
347 *****************************************************************************************
348 * @brief Handles reception of the disconnected event.
349 *
350 * @param[in] conn_idx: Index of the connection.
351 *****************************************************************************************
352 */
pcs_disconnected(uint8_t conn_idx,uint8_t disconn_reason)353 static void pcs_disconnected(uint8_t conn_idx, uint8_t disconn_reason)
354 {
355 pcs_evt_t event = {
356 .conn_idx = conn_idx,
357 .evt_type = PCS_EVT_DISCONNECTED,
358 .p_data = &disconn_reason,
359 .length = sizeof(uint8_t)
360 };
361
362 if (s_pcs_env.pcs_init.evt_handler) {
363 s_pcs_env.pcs_init.evt_handler(&event);
364 }
365 }
366
367 /**
368 *****************************************************************************************
369 * @brief Handles reception of the complete event.
370 *
371 * @param[in] conn_idx: Connection index.
372 * @param[in] status: The status of sending notifications or indications.
373 * @param[in] p_ntf_ind: Pointer to the structure of the complete event.
374 *****************************************************************************************
375 */
pcs_ntf_ind_cb(uint8_t conn_idx,uint8_t status,const ble_gatts_ntf_ind_t * p_ntf_ind)376 static void pcs_ntf_ind_cb(uint8_t conn_idx, uint8_t status, const ble_gatts_ntf_ind_t *p_ntf_ind)
377 {
378 if (s_pcs_env.pcs_init.evt_handler != NULL) {
379 pcs_evt_t event;
380 event.conn_idx = conn_idx;
381
382 if (BLE_SUCCESS == status && BLE_GATT_NOTIFICATION == p_ntf_ind->type) {
383 event.evt_type = PCS_EVT_TX_DATA_SENT;
384 s_pcs_env.pcs_init.evt_handler(&event);
385 }
386 }
387 }
388
389 /*
390 * GLOBAL FUNCTION DEFINITIONS
391 *****************************************************************************************
392 */
pcs_tx_data_send(uint8_t conn_idx,uint8_t * p_data,uint16_t length)393 sdk_err_t pcs_tx_data_send(uint8_t conn_idx, uint8_t *p_data, uint16_t length)
394 {
395 sdk_err_t error_code = SDK_ERR_NTF_DISABLED;
396 gatts_noti_ind_t send_rsp;
397
398 if (PRF_CLI_START_NTF == s_pcs_env.tx_ntf_cfg[conn_idx]) {
399 // Fill in the parameter structure
400 send_rsp.type = BLE_GATT_NOTIFICATION;
401 send_rsp.handle = prf_find_handle_by_idx(PCS_IDX_TX_VAL, s_pcs_env.start_hdl, (uint8_t *)&s_char_mask);
402
403 // Pack measured value in database
404 send_rsp.length = length;
405 send_rsp.value = p_data;
406
407 // Send notification to peer device
408 error_code = ble_gatts_noti_ind(conn_idx, &send_rsp);
409 }
410
411 return error_code;
412 }
413
pcs_setting_reply(uint8_t conn_idx,uint8_t * p_data,uint16_t length)414 sdk_err_t pcs_setting_reply(uint8_t conn_idx, uint8_t *p_data, uint16_t length)
415 {
416 sdk_err_t error_code = SDK_ERR_IND_DISABLED;
417 gatts_noti_ind_t send_cmd;
418
419 if (PRF_CLI_START_IND == s_pcs_env.setting_ind_cfg[conn_idx]) {
420 // Fill in the parameter structure
421 send_cmd.type = BLE_GATT_INDICATION;
422 send_cmd.handle = prf_find_handle_by_idx(PCS_IDX_SETTING_VAL, s_pcs_env.start_hdl, (uint8_t *)&s_char_mask);
423
424 // Pack measured value in database
425 send_cmd.length = length;
426 send_cmd.value = p_data;
427
428 // Send indication to peer device
429 error_code = ble_gatts_noti_ind(conn_idx, &send_cmd);
430 }
431
432 return error_code;
433 }
434
pcs_service_init(pcs_init_t * p_pcs_init)435 sdk_err_t pcs_service_init(pcs_init_t *p_pcs_init)
436 {
437 sdk_err_t ret;
438 if (p_pcs_init == NULL) {
439 return SDK_ERR_POINTER_NULL;
440 }
441
442 ret = memcpy_s(&s_pcs_env.pcs_init, sizeof(pcs_init_t), p_pcs_init, sizeof(pcs_init_t));
443 if (ret < 0) {
444 return ret;
445 }
446
447 return ble_server_prf_add(&pcs_prf_info);
448 }
449