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