1 /**
2 *****************************************************************************************
3 *
4 * @file bps.c
5 *
6 * @brief Blood Pressure 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 "bps.h"
43 #include "ble_prf_types.h"
44 #include "ble_prf_utils.h"
45 #include "utility.h"
46 #define OFFSET_12 12
47 /*
48 * ENUMERATIONS
49 *****************************************************************************************
50 */
51 /**@brief Blood Pressure Service Attributes Indexes. */
52 enum bps_attr_idx_t {
53 BPS_IDX_SVC,
54
55 BPS_IDX_BP_MEAS_CHAR,
56 BPS_IDX_BP_MEAS_VAL,
57 BPS_IDX_BP_MEAS_IND_CFG,
58
59 BPS_IDX_INTM_CUFF_PRESS_CHAR,
60 BPS_IDX_INTM_CUFF_PRESS_VAL,
61 BPS_IDX_INTM_CUFF_PRESS_NTF_CFG,
62
63 BPS_IDX_BP_FEATURE_CHAR,
64 BPS_IDX_BP_FEATURE_VAL,
65
66 BPS_IDX_NB,
67 };
68
69 /**@brief Blood Pressure Measurement Flags bits. */
70 enum bps_meas_flag_t {
71 BPS_MEAS_BLOOD_PRESSURE_UNITS_FLAG_BIT = (0x01 << 0), /**< Blood Pressure Units Flag bit. */
72 BPS_MEAS_TIME_STAMP_FLAG_BIT = (0x01 << 1), /**< Time Stamp Flag bit. */
73 BPS_MEAS_PULSE_RATE_FLAG_BIT = (0x01 << 2), /**< Pulse Rate Flag bit. */
74 BPS_MEAS_USER_ID_FLAG_BIT = (0x01 << 3), /**< User ID Flag bit. */
75 BPS_MEAS_MEASUREMENT_STATUS_FLAG_BIT = (0x01 << 4), /**< Measurement Status Flag bit. */
76 };
77
78 /*
79 * STRUCTURES
80 *****************************************************************************************
81 */
82 /**@brief Blood Pressure Service environment variable. */
83 struct bps_env_t {
84 bps_init_t bps_init; /**< Blood Pressure Service initialization variables. */
85 uint16_t start_hdl; /**< Start handle of Blood Pressure Service. */
86 uint16_t
87 ntf_ind_cfg[BPS_CONNECTION_MAX]; /**< Notification and indication configuration for each connections. */
88 };
89
90 /*
91 * LOCAL FUNCTION DECLARATION
92 *****************************************************************************************
93 */
94 static sdk_err_t bps_init(void);
95 static void bps_read_att_cb(uint8_t conn_idx, const gatts_read_req_cb_t *p_param);
96 static void bps_write_att_cb(uint8_t conn_idx, const gatts_write_req_cb_t *p_param);
97 static void bps_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value);
98 /*
99 * LOCAL VARIABLE DEFINITIONS
100 *****************************************************************************************
101 */
102 static struct bps_env_t s_bps_env;
103
104 /**@brief Full BPS Database Description which is used to add attributes into theATT database. */
105 static const attm_desc_t bps_attr_tab[BPS_IDX_NB] = {
106 // Blood Pressure Service Declaration
107 [BPS_IDX_SVC] = {BLE_ATT_DECL_PRIMARY_SERVICE, READ_PERM_UNSEC, 0, 0},
108
109 // Blood Pressure Measurement Characteristic Declaration
110 [BPS_IDX_BP_MEAS_CHAR] = {BLE_ATT_DECL_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
111 // Blood Pressure Measurement Characteristic Value
112 [BPS_IDX_BP_MEAS_VAL] = {
113 BLE_ATT_CHAR_BLOOD_PRESSURE_MEAS,
114 #ifdef PTS_AUTO_TEST
115 INDICATE_PERM_UNSEC,
116 #else
117 INDICATE_PERM(NOAUTH),
118 #endif
119 ATT_VAL_LOC_USER,
120 BPS_BP_MEAS_MAX_LEN
121 },
122 // Blood Pressure Measurement Characteristic - Client Characteristic Configuration Descriptor
123 [BPS_IDX_BP_MEAS_IND_CFG] = {
124 BLE_ATT_DESC_CLIENT_CHAR_CFG,
125 #ifdef PTS_AUTO_TEST
126 READ_PERM_UNSEC | WRITE_REQ_PERM_UNSEC,
127 #else
128 READ_PERM(NOAUTH) | WRITE_REQ_PERM(NOAUTH),
129 #endif
130 0,
131 0
132 },
133
134 // Intermediate Cuff Pressure Characteristic Declaration
135 [BPS_IDX_INTM_CUFF_PRESS_CHAR] = {BLE_ATT_DECL_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
136 // Intermediate Cuff Pressure Characteristic Value
137 [BPS_IDX_INTM_CUFF_PRESS_VAL] = {
138 BLE_ATT_CHAR_INTERMEDIATE_CUFF_PRESSURE,
139 #ifdef PTS_AUTO_TEST
140 NOTIFY_PERM_UNSEC,
141 #else
142 NOTIFY_PERM(NOAUTH),
143 #endif
144 ATT_VAL_LOC_USER,
145 BPS_BP_MEAS_MAX_LEN
146 },
147 // Intermediate Cuff Pressure Characteristic - Client Characteristic Configuration Descriptor
148 [BPS_IDX_INTM_CUFF_PRESS_NTF_CFG] = {
149 BLE_ATT_DESC_CLIENT_CHAR_CFG,
150 #ifdef PTS_AUTO_TEST
151 READ_PERM_UNSEC | WRITE_REQ_PERM_UNSEC,
152 #else
153 READ_PERM(NOAUTH) | WRITE_REQ_PERM(NOAUTH),
154 #endif
155 0,
156 0
157 },
158
159 // Blood Pressure Feature Characteristic Declaration
160 [BPS_IDX_BP_FEATURE_CHAR] = {BLE_ATT_DECL_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
161 // Blood Pressure Feature Characteristic Value
162 [BPS_IDX_BP_FEATURE_VAL] = {
163 BLE_ATT_CHAR_BLOOD_PRESSURE_FEATURE,
164 #ifdef PTS_AUTO_TEST
165 READ_PERM_UNSEC,
166 #else
167 READ_PERM(NOAUTH),
168 #endif
169 ATT_VAL_LOC_USER,
170 sizeof(uint16_t)
171 },
172 };
173
174 /**@brief BPS interface required by profile manager. */
175 static ble_prf_manager_cbs_t bps_mgr_cbs = {
176 (prf_init_func_t)bps_init,
177 NULL,
178 NULL
179 };
180
181 /**@brief BPS GATT Server Callbacks. */
182 static gatts_prf_cbs_t bps_gatts_cbs = {
183 bps_read_att_cb,
184 bps_write_att_cb,
185 NULL,
186 NULL,
187 bps_cccd_set_cb,
188 };
189
190 /**@brief BPS Information. */
191 static const prf_server_info_t bps_prf_info = {
192 .max_connection_nb = BPS_CONNECTION_MAX,
193 .manager_cbs = &bps_mgr_cbs,
194 .gatts_prf_cbs = &bps_gatts_cbs
195 };
196
197 /*
198 * LOCAL FUNCTION DEFINITIONS
199 *****************************************************************************************
200 */
201 /**
202 *****************************************************************************************
203 * @brief Initialize Blood Pressure service and create DB in ATT.
204 *
205 * @retval ::Error code to know if profile initialization succeed or not.
206 *****************************************************************************************
207 */
bps_init(void)208 static sdk_err_t bps_init(void)
209 {
210 // The start hanlde must be set with PRF_INVALID_HANDLE to be allocated automatically by BLE Stack.
211 uint16_t start_hdl = PRF_INVALID_HANDLE;
212 const uint8_t bps_svc_uuid[] = BLE_ATT_16_TO_16_ARRAY(BLE_ATT_SVC_BLOOD_PRESSURE);
213 sdk_err_t error_code;
214 gatts_create_db_t gatts_db;
215
216 error_code = memset_s(&gatts_db, sizeof(gatts_db), 0, sizeof(gatts_db));
217 if (error_code < 0) {
218 return error_code;
219 }
220
221 gatts_db.shdl = &start_hdl;
222 gatts_db.uuid = bps_svc_uuid;
223 gatts_db.attr_tab_cfg = (uint8_t*)&(s_bps_env.bps_init.char_mask);
224 gatts_db.max_nb_attr = BPS_IDX_NB;
225 gatts_db.srvc_perm = 0;
226 gatts_db.attr_tab_type = SERVICE_TABLE_TYPE_16;
227 gatts_db.attr_tab.attr_tab_16 = bps_attr_tab;
228
229 error_code = ble_gatts_srvc_db_create(&gatts_db);
230 if (SDK_SUCCESS == error_code) {
231 s_bps_env.start_hdl = *gatts_db.shdl;
232 }
233
234 return error_code;
235 }
236
237 /**
238 *****************************************************************************************
239 * @brief Handles reception of the attribute info request message.
240 *
241 * @param[in] conn_idx: Connection index
242 * @param[in] p_param: The parameters of the read request.
243 *****************************************************************************************
244 */
bps_read_att_cb(uint8_t conn_idx,const gatts_read_req_cb_t * p_param)245 static void bps_read_att_cb(uint8_t conn_idx, const gatts_read_req_cb_t *p_param)
246 {
247 gatts_read_cfm_t cfm;
248 uint8_t handle = p_param->handle;
249 uint8_t tab_index = prf_find_idx_by_handle(handle, s_bps_env.start_hdl,
250 BPS_IDX_NB,
251 (uint8_t *)&s_bps_env.bps_init.char_mask);
252
253 cfm.handle = handle;
254 cfm.status = BLE_SUCCESS;
255
256 switch (tab_index) {
257 case BPS_IDX_BP_MEAS_IND_CFG:
258 cfm.length = sizeof(uint16_t);
259 cfm.value = (uint8_t *)&s_bps_env.ntf_ind_cfg[conn_idx];
260 break;
261
262 case BPS_IDX_INTM_CUFF_PRESS_NTF_CFG:
263 cfm.length = sizeof(uint16_t);
264 cfm.value = (uint8_t *)&s_bps_env.ntf_ind_cfg[conn_idx];
265 break;
266
267 case BPS_IDX_BP_FEATURE_VAL:
268 if (s_bps_env.bps_init.evt_handler) {
269 s_bps_env.bps_init.evt_handler(conn_idx, BPS_EVT_READ_BL_PRESSURE_FEATURE);
270 }
271 cfm.length = sizeof(uint16_t);
272 cfm.value = (uint8_t *)&s_bps_env.bps_init.bp_feature;
273 break;
274
275 default:
276 cfm.length = 0;
277 cfm.status = BLE_ATT_ERR_INVALID_HANDLE;
278 break;
279 }
280
281 ble_gatts_read_cfm(conn_idx, &cfm);
282 }
283
284 /**
285 *****************************************************************************************
286 * @brief Handles reception of the write request.
287 *
288 * @param[in]: conn_idx: Connection index
289 * @param[in]: p_param: The parameters of the write request.
290 *****************************************************************************************
291 */
bps_write_att_cb(uint8_t conn_idx,const gatts_write_req_cb_t * p_param)292 static void bps_write_att_cb(uint8_t conn_idx, const gatts_write_req_cb_t *p_param)
293 {
294 uint16_t handle = p_param->handle;
295 uint16_t tab_index = 0;
296 uint16_t cccd_value = 0;
297 bps_evt_type_t event;
298 gatts_write_cfm_t cfm;
299
300 tab_index = prf_find_idx_by_handle(handle,
301 s_bps_env.start_hdl,
302 BPS_IDX_NB,
303 (uint8_t *)&s_bps_env.bps_init.char_mask);
304 cfm.handle = handle;
305 cfm.status = BLE_SUCCESS;
306
307 switch (tab_index) {
308 case BPS_IDX_BP_MEAS_IND_CFG:
309 cccd_value = le16toh(&p_param->value[0]);
310 event = ((PRF_CLI_START_IND == cccd_value) ?\
311 BPS_EVT_BP_MEAS_INDICATION_ENABLED :\
312 BPS_EVT_BP_MEAS_INDICATION_DISABLED);
313 s_bps_env.ntf_ind_cfg[conn_idx] = cccd_value;
314 break;
315
316 case BPS_IDX_INTM_CUFF_PRESS_NTF_CFG:
317 cccd_value = le16toh(&p_param->value[0]);
318 event = ((PRF_CLI_START_NTF == cccd_value) ?\
319 BPS_EVT_INTM_CUFF_PRESS_NTF_ENABLED :\
320 BPS_EVT_INTM_CUFF_PRESS_NTF_DISABLED);
321 \
322 s_bps_env.ntf_ind_cfg[conn_idx] = cccd_value;
323 break;
324
325 default:
326 cfm.status = BLE_ATT_ERR_INVALID_HANDLE;
327 break;
328 }
329
330 if (BLE_ATT_ERR_INVALID_HANDLE != cfm.status && BPS_EVT_INVALID != event && s_bps_env.bps_init.evt_handler) {
331 s_bps_env.bps_init.evt_handler(conn_idx, event);
332 }
333 ble_gatts_write_cfm(conn_idx, &cfm);
334 }
335
336 /**
337 *****************************************************************************************
338 * @brief Handles reception of the cccd recover request.
339 *
340 * @param[in]: conn_idx: Connection index
341 * @param[in]: handle: The handle of cccd attribute.
342 * @param[in]: cccd_value: The value of cccd attribute.
343 *****************************************************************************************
344 */
bps_cccd_set_cb(uint8_t conn_idx,uint16_t handle,uint16_t cccd_value)345 static void bps_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value)
346 {
347 uint16_t tab_index = 0;
348 bps_evt_type_t event;
349
350 if (!prf_is_cccd_value_valid(cccd_value)) {
351 return;
352 }
353
354 tab_index = prf_find_idx_by_handle(handle,
355 s_bps_env.start_hdl,
356 BPS_IDX_NB,
357 (uint8_t *)&s_bps_env.bps_init.char_mask);
358
359 switch (tab_index) {
360 case BPS_IDX_BP_MEAS_IND_CFG:
361 event = ((PRF_CLI_START_IND == cccd_value) ?\
362 BPS_EVT_BP_MEAS_INDICATION_ENABLED :\
363 BPS_EVT_BP_MEAS_INDICATION_DISABLED);
364 s_bps_env.ntf_ind_cfg[conn_idx] = cccd_value;
365 break;
366
367 case BPS_IDX_INTM_CUFF_PRESS_NTF_CFG:
368 event = ((PRF_CLI_START_NTF == cccd_value) ?\
369 BPS_EVT_INTM_CUFF_PRESS_NTF_ENABLED :\
370 BPS_EVT_INTM_CUFF_PRESS_NTF_DISABLED);
371 \
372 s_bps_env.ntf_ind_cfg[conn_idx] = cccd_value;
373 break;
374
375 default:
376 event = BPS_EVT_INVALID;
377 break;
378 }
379
380 if (BPS_EVT_INVALID != event && s_bps_env.bps_init.evt_handler) {
381 s_bps_env.bps_init.evt_handler(conn_idx, event);
382 }
383 }
384
385 /**
386 *****************************************************************************************
387 * @brief Encode a Blood Pressure Measurement.
388 *
389 * @param[in] p_bps_meas: Measurement to be encoded.
390 * @param[out] p_encoded_buffer: Buffer where the encoded data will be written.
391 *
392 * @retval ::Size of encoded data.
393 *****************************************************************************************
394 */
bps_measurement_encode(bps_meas_t * p_meas,uint8_t * p_encoded_buffer)395 static uint16_t bps_measurement_encode(bps_meas_t *p_meas, uint8_t *p_encoded_buffer)
396 {
397 uint8_t flags = 0;
398 uint16_t length = 1;
399 uint16_t encoded_sfloat = 0;
400
401 // Set measurement units flag
402 if (p_meas->bl_unit_in_kpa) {
403 flags |= BPS_MEAS_BLOOD_PRESSURE_UNITS_FLAG_BIT;
404 }
405
406 // Blood Pressure Measurement - Systolic
407 encoded_sfloat = ((p_meas->systolic.exponent << OFFSET_12) & 0xF000) |
408 ((p_meas->systolic.mantissa << 0) & 0x0FFF);
409 p_encoded_buffer[length++] = LO_U16(encoded_sfloat);
410 p_encoded_buffer[length++] = HI_U16(encoded_sfloat);
411
412 // Blood Pressure Measurement - Diastolic
413 encoded_sfloat = ((p_meas->diastolic.exponent << OFFSET_12) & 0xF000) |
414 ((p_meas->diastolic.mantissa << 0) & 0x0FFF);
415 p_encoded_buffer[length++] = LO_U16(encoded_sfloat);
416 p_encoded_buffer[length++] = HI_U16(encoded_sfloat);
417
418 // Blood Pressure Measurement - Mean Arterial Pressure
419 encoded_sfloat = ((p_meas->mean_arterial_pr.exponent << OFFSET_12) & 0xF000) |
420 ((p_meas->mean_arterial_pr.mantissa << 0) & 0x0FFF);
421 p_encoded_buffer[length++] = LO_U16(encoded_sfloat);
422 p_encoded_buffer[length++] = HI_U16(encoded_sfloat);
423
424 // Time Stamp field
425 if (p_meas->time_stamp_present) {
426 flags |= BPS_MEAS_TIME_STAMP_FLAG_BIT;
427 p_encoded_buffer[length++] = LO_U16(p_meas->time_stamp.year);
428 p_encoded_buffer[length++] = HI_U16(p_meas->time_stamp.year);
429 p_encoded_buffer[length++] = p_meas->time_stamp.month;
430 p_encoded_buffer[length++] = p_meas->time_stamp.day;
431 p_encoded_buffer[length++] = p_meas->time_stamp.hour;
432 p_encoded_buffer[length++] = p_meas->time_stamp.min;
433 p_encoded_buffer[length++] = p_meas->time_stamp.sec;
434 }
435
436 // Pulse Rate
437 if (p_meas->pulse_rate_present) {
438 flags |= BPS_MEAS_PULSE_RATE_FLAG_BIT;
439 encoded_sfloat = ((p_meas->pulse_rate.exponent << OFFSET_12) & 0xF000) |
440 ((p_meas->pulse_rate.mantissa << 0) & 0x0FFF);
441 p_encoded_buffer[length++] = LO_U16(encoded_sfloat);
442 p_encoded_buffer[length++] = HI_U16(encoded_sfloat);
443 }
444
445 // User ID
446 if (p_meas->user_id_present) {
447 flags |= BPS_MEAS_USER_ID_FLAG_BIT;
448 p_encoded_buffer[length++] = p_meas->user_id;
449 }
450
451 // Measurement Status
452 if (p_meas->meas_status_present) {
453 flags |= BPS_MEAS_MEASUREMENT_STATUS_FLAG_BIT;
454 p_encoded_buffer[length++] = LO_U16(p_meas->meas_status);
455 p_encoded_buffer[length++] = HI_U16(p_meas->meas_status);
456 }
457
458 // Flags field
459 p_encoded_buffer[0] = flags;
460
461 return length;
462 }
463
464 /*
465 * GLOBAL FUNCTION DEFINITIONS
466 *****************************************************************************************
467 */
bps_measurement_send(uint8_t conidx,bps_meas_t * p_meas)468 sdk_err_t bps_measurement_send(uint8_t conidx, bps_meas_t *p_meas)
469 {
470 sdk_err_t error_code = SDK_ERR_IND_DISABLED;
471 uint8_t encoded_bps_meas[BPS_BP_MEAS_MAX_LEN] = {0};
472 gatts_noti_ind_t bps_ind;
473
474 uint16_t length = bps_measurement_encode(p_meas, encoded_bps_meas);
475
476 if (PRF_CLI_START_IND == (s_bps_env.ntf_ind_cfg[conidx] & PRF_CLI_START_IND)) {
477 bps_ind.type = BLE_GATT_INDICATION;
478 bps_ind.handle = prf_find_handle_by_idx(BPS_IDX_BP_MEAS_VAL,
479 s_bps_env.start_hdl,
480 (uint8_t *)&s_bps_env.bps_init.char_mask);
481 bps_ind.length = length;
482 bps_ind.value = encoded_bps_meas;
483
484 error_code = ble_gatts_noti_ind(conidx, &bps_ind);
485 }
486
487 return error_code;
488 }
489
bps_service_init(bps_init_t * p_bps_init)490 sdk_err_t bps_service_init(bps_init_t *p_bps_init)
491 {
492 sdk_err_t ret;
493 if (p_bps_init == NULL) {
494 return SDK_ERR_POINTER_NULL;
495 }
496
497 ret = memcpy_s(&s_bps_env.bps_init, sizeof(bps_init_t), p_bps_init, sizeof(bps_init_t));
498 if (ret < 0) {
499 return ret;
500 }
501
502 return ble_server_prf_add(&bps_prf_info);
503 }
504