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