• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  *****************************************************************************************
3  *
4  * @file ths.c
5  *
6  * @brief Throughput Server 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 "ths.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 SETTINGS_CHAR_VALUE_LEN           10   /**< Maximum length of the value of Settings characteristic. */
52 #define THS_TX_CHARACTERISTIC_UUID        {0x1B, 0xD7, 0x90, 0xEC, 0xE8, 0xB9, 0x75, 0x80, 0x0A, 0x46, 0x44, \
53                                            0xD3, 0x02, 0x03, 0xED, 0xA6}
54 #define THS_RX_CHARACTERISTIC_UUID        {0x1B, 0xD7, 0x90, 0xEC, 0xE8, 0xB9, 0x75, 0x80, 0x0A, 0x46, 0x44, \
55                                            0xD3, 0x03, 0x03, 0xED, 0xA6}
56 #define THS_SETTINGS_CHARACTERISTIC_UUID  {0x1B, 0xD7, 0x90, 0xEC, 0xE8, 0xB9, 0x75, 0x80, 0x0A, 0x46, 0x44, \
57                                            0xD3, 0x04, 0x03, 0xED, 0xA6}
58 #define THS_TOGGLE_CHARACTERISTIC_UUID    {0x1B, 0xD7, 0x90, 0xEC, 0xE8, 0xB9, 0x75, 0x80, 0x0A, 0x46, 0x44, \
59                                            0xD3, 0x05, 0x03, 0xED, 0xA6}
60 
61 /**@brief Macros for conversion of 128bit to 16bit UUID. */
62 #define ATT_128_PRIMARY_SERVICE     BLE_ATT_16_TO_128_ARRAY(BLE_ATT_DECL_PRIMARY_SERVICE)
63 #define ATT_128_CHARACTERISTIC      BLE_ATT_16_TO_128_ARRAY(BLE_ATT_DECL_CHARACTERISTIC)
64 #define ATT_128_CLIENT_CHAR_CFG     BLE_ATT_16_TO_128_ARRAY(BLE_ATT_DESC_CLIENT_CHAR_CFG)
65 
66 /*
67  * ENUMERATIONS
68  *****************************************************************************************
69  */
70 /**@brief Throughput Service Attributes Indexes. */
71 enum ths_attr_idx_t {
72     THS_IDX_SVC,
73 
74     THS_IDX_TX_CHAR,
75     THS_IDX_TX_VAL,
76     THS_IDX_TX_CFG,
77 
78     THS_IDX_RX_CHAR,
79     THS_IDX_RX_VAL,
80 
81     THS_IDX_SETTINGS_CHAR,
82     THS_IDX_SETTINGS_VAL,
83     THS_IDX_SETTINGS_CFG,
84 
85     THS_IDX_TOGGLE_CHAR,
86     THS_IDX_TOGGLE_VAL,
87 
88     THS_IDX_NB,
89 };
90 
91 /*
92  * STRUCTURES
93  *****************************************************************************************
94  */
95 /**@brief Throughput service environment variable. */
96 struct ths_env_t {
97     ths_init_t    ths_init;                             /**< Throughput Service initialization variables. */
98     uint16_t      start_hdl;                            /**< Service start handle. */
99     uint16_t      data_ntf_cfg[THS_CONNECTION_MAX];     /**< Notification configuration for sending the data. */
100     uint16_t
101     settings_ntf_cfg[THS_CONNECTION_MAX]; /**< Notification configuration for notifying the settings change. */
102 };
103 
104 /*
105 * LOCAL FUNCTION DECLARATION
106 *****************************************************************************************
107 */
108 static sdk_err_t   ths_init(void);
109 static void        ths_write_att_cb(uint8_t conn_idx, const gatts_write_req_cb_t *p_param);
110 static void        ths_read_att_cb(uint8_t conn_idx, const gatts_read_req_cb_t *p_param);
111 static void        ths_gatts_cmpl_cb(uint8_t conn_idx, uint8_t status, const ble_gatts_ntf_ind_t *p_ntf_ind);
112 static void        ths_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value);
113 
114 /*
115  * LOCAL VARIABLE DEFINITIONS
116  *****************************************************************************************
117  */
118 static struct ths_env_t s_ths_env;
119 static uint8_t          s_noti_cfg_idx;
120 static const uint16_t   s_char_mask = 0x07FF;
121 
122 /**@brief Full THS Database Description - Used to add attributes into the database. */
123 static const attm_desc_128_t ths_attr_tab[THS_IDX_NB] = {
124     // ths service
125     [THS_IDX_SVC]           = {ATT_128_PRIMARY_SERVICE, READ_PERM_UNSEC, 0, 0},
126 
127     // ths tx Declaration
128     [THS_IDX_TX_CHAR]       = {ATT_128_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
129     // ths tx Value
130     [THS_IDX_TX_VAL]        = {
131         THS_TX_CHARACTERISTIC_UUID,
132         (NOTIFY_PERM_UNSEC),
133         (ATT_VAL_LOC_USER | ATT_UUID_TYPE_SET(UUID_TYPE_128)),
134         THS_MAX_DATA_LEN
135     },
136     // ths tx Characteristic Configuration
137     [THS_IDX_TX_CFG]        = {
138         ATT_128_CLIENT_CHAR_CFG,
139         (READ_PERM_UNSEC | WRITE_REQ_PERM_UNSEC | WRITE_CMD_PERM_UNSEC),
140         0,
141         0
142     },
143 
144     // ths rx
145     [THS_IDX_RX_CHAR]       = {ATT_128_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
146     // ths rx Value
147     [THS_IDX_RX_VAL]        = {
148         THS_RX_CHARACTERISTIC_UUID,
149         WRITE_CMD_PERM_UNSEC,
150         (ATT_VAL_LOC_USER | ATT_UUID_TYPE_SET(UUID_TYPE_128)),
151         THS_MAX_DATA_LEN
152     },
153 
154     // ths settings
155     [THS_IDX_SETTINGS_CHAR] = {ATT_128_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
156     // ths settings Value
157     [THS_IDX_SETTINGS_VAL]  = {
158         THS_SETTINGS_CHARACTERISTIC_UUID,
159         (WRITE_CMD_PERM_UNSEC | NOTIFY_PERM_UNSEC),
160         (ATT_VAL_LOC_USER | ATT_UUID_TYPE_SET(UUID_TYPE_128)),
161         SETTINGS_CHAR_VALUE_LEN
162     },
163     // ths settings cfg
164     [THS_IDX_SETTINGS_CFG]  = {
165         ATT_128_CLIENT_CHAR_CFG,
166         (READ_PERM_UNSEC | WRITE_REQ_PERM_UNSEC | WRITE_CMD_PERM_UNSEC),
167         0,
168         0
169     },
170 
171     // ths toggle
172     [THS_IDX_TOGGLE_CHAR]   = {ATT_128_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
173     // ths toggle Value
174     [THS_IDX_TOGGLE_VAL]    = {
175         THS_TOGGLE_CHARACTERISTIC_UUID,
176         WRITE_CMD_PERM_UNSEC,
177         (ATT_VAL_LOC_USER | ATT_UUID_TYPE_SET(UUID_TYPE_128)),
178         sizeof(uint8_t)
179     },
180 };
181 
182 /**@brief Throughput Service interface required by profile manager. */
183 static ble_prf_manager_cbs_t ths_mgr_cbs = {
184     (prf_init_func_t)ths_init,
185     NULL,
186     NULL
187 };
188 
189 /**@brief Throughput GATT Server Callbacks. */
190 static gatts_prf_cbs_t ths_gatts_cbs = {
191     ths_read_att_cb,
192     ths_write_att_cb,
193     NULL,
194     ths_gatts_cmpl_cb,
195     ths_cccd_set_cb
196 };
197 
198 /**@brief Throughput Service Information. */
199 static const prf_server_info_t ths_prf_info = {
200     .max_connection_nb = THS_CONNECTION_MAX,
201     .manager_cbs       = &ths_mgr_cbs,
202     .gatts_prf_cbs     = &ths_gatts_cbs
203 };
204 
205 /*
206  * LOCAL FUNCTION DEFINITIONS
207  *****************************************************************************************
208  */
209 /**
210  *****************************************************************************************
211  * @brief Initialize throughput service, and create database in ATT.
212  *
213  * @return Error code to know if profile initialization succeed or not.
214  *****************************************************************************************
215  */
ths_init(void)216 static sdk_err_t ths_init(void)
217 {
218     const uint8_t     ths_svc_uuid[] = {THS_SERVICE_UUID};
219     uint16_t          start_hdl = PRF_INVALID_HANDLE;
220     sdk_err_t         error_code;
221     gatts_create_db_t gatts_db;
222 
223     error_code = memset_s(&gatts_db, sizeof(gatts_db), 0, sizeof(gatts_db));
224     if (error_code < 0) {
225         return error_code;
226     }
227 
228     gatts_db.shdl                  = &start_hdl;
229     gatts_db.uuid                  = ths_svc_uuid;
230     gatts_db.attr_tab_cfg          = (uint8_t *)&s_char_mask;
231     gatts_db.max_nb_attr           = THS_IDX_NB;
232     gatts_db.srvc_perm             = SRVC_UUID_TYPE_SET(UUID_TYPE_128);
233     gatts_db.attr_tab_type         = SERVICE_TABLE_TYPE_128;
234     gatts_db.attr_tab.attr_tab_128 = ths_attr_tab;
235 
236     error_code = ble_gatts_srvc_db_create(&gatts_db);
237     if (SDK_SUCCESS == error_code) {
238         s_ths_env.start_hdl = *gatts_db.shdl;
239     }
240 
241     return error_code;
242 }
243 
244 /**
245  *****************************************************************************************
246  * @brief Handles reception of the attribute info request message.
247  *
248  * @param[in] conn_idx: Connection index.
249  * @param[in] p_param:  Pointer to the parameters of the read request.
250  *****************************************************************************************
251  */
ths_read_att_cb(uint8_t conn_idx,const gatts_read_req_cb_t * p_param)252 static void   ths_read_att_cb(uint8_t conn_idx, const gatts_read_req_cb_t *p_param)
253 {
254     gatts_read_cfm_t cfm;
255     uint8_t          handle    = p_param->handle;
256     uint8_t          tab_index = 0;
257 
258     tab_index  = prf_find_idx_by_handle(handle, s_ths_env.start_hdl, THS_IDX_NB, (uint8_t *)&s_char_mask);
259     cfm.handle = handle;
260     cfm.status = BLE_SUCCESS;
261 
262     switch (tab_index) {
263         case THS_IDX_TX_CFG:
264             cfm.length = sizeof(uint16_t);
265             cfm.value  = (uint8_t *)&s_ths_env.data_ntf_cfg[conn_idx];
266             break;
267 
268         case THS_IDX_SETTINGS_CFG:
269             cfm.length = sizeof(uint16_t);
270             cfm.value  = (uint8_t *)&s_ths_env.settings_ntf_cfg[conn_idx];
271             break;
272 
273         default:
274             cfm.length = 0;
275             cfm.status = BLE_ATT_ERR_INVALID_HANDLE;
276             break;
277     }
278 
279     ble_gatts_read_cfm(conn_idx, &cfm);
280 }
281 
282 /**
283  *****************************************************************************************
284  * @brief Handles reception of the write request.
285  *
286  * @param[in] conn_idx: Connection index
287  * @param[in] p_param:  Pointer to the parameters of the write request.
288  *
289  * @return If the request was consumed or not.
290  *****************************************************************************************
291  */
ths_write_att_cb(uint8_t conn_idx,const gatts_write_req_cb_t * p_param)292 static void   ths_write_att_cb(uint8_t conn_idx, const gatts_write_req_cb_t *p_param)
293 {
294     uint8_t           handle    = p_param->handle;
295     uint8_t           tab_index = 0;
296     ths_evt_t         event;
297     gatts_write_cfm_t cfm;
298 
299     tab_index  = prf_find_idx_by_handle(handle, s_ths_env.start_hdl, THS_IDX_NB, (uint8_t *)&s_char_mask);
300     cfm.handle = handle;
301     cfm.status = BLE_SUCCESS;
302 
303     event.conn_idx = conn_idx;
304     event.evt_type = THS_EVT_INVALID;
305 
306     switch (tab_index) {
307         case THS_IDX_TX_CFG:
308             s_ths_env.data_ntf_cfg[conn_idx] = le16toh(&p_param->value[0]);
309             break;
310 
311         case THS_IDX_SETTINGS_CFG:
312             s_ths_env.settings_ntf_cfg[conn_idx] = le16toh(&p_param->value[0]);
313             break;
314 
315         case THS_IDX_RX_VAL:
316             event.evt_type = THS_EVT_DATA_RECEIVED;
317             break;
318 
319         case THS_IDX_SETTINGS_VAL:
320             event.evt_type = THS_EVT_SETTINGS_CHANGED;
321             if (THS_SETTINGS_TYPE_TRANS_MODE == p_param->value[0]) {
322                 s_ths_env.ths_init.transport_mode = (ths_transport_mode_t)p_param->value[1];
323             }
324             break;
325 
326         case THS_IDX_TOGGLE_VAL:
327             event.evt_type = THS_EVT_TOGGLE_SET;
328             break;
329 
330         default:
331             cfm.status = BLE_ATT_ERR_INVALID_HANDLE;
332             break;
333     }
334 
335     if (BLE_ATT_ERR_INVALID_HANDLE != cfm.status && THS_EVT_INVALID != event.evt_type &&
336         s_ths_env.ths_init.evt_handler) {
337         event.length   = p_param->length;
338         event.p_data   = (uint8_t *)p_param->value;
339         s_ths_env.ths_init.evt_handler(&event);
340     }
341 
342     ble_gatts_write_cfm(conn_idx, &cfm);
343 }
344 
345 /**
346  *****************************************************************************************
347  * @brief Handles reception of the cccd recover request.
348  *
349  * @param[in]: conn_idx:   Connection index
350  * @param[in]: handle:     The handle of cccd attribute.
351  * @param[in]: cccd_value: The value of cccd attribute.
352  *****************************************************************************************
353  */
ths_cccd_set_cb(uint8_t conn_idx,uint16_t handle,uint16_t cccd_value)354 static void ths_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value)
355 {
356     uint8_t  tab_index = 0;
357 
358     if (!prf_is_cccd_value_valid(cccd_value)) {
359         return;
360     }
361 
362     tab_index  = prf_find_idx_by_handle(handle, s_ths_env.start_hdl, THS_IDX_NB, (uint8_t *)&s_char_mask);
363 
364     switch (tab_index) {
365         case THS_IDX_TX_CFG:
366             s_ths_env.data_ntf_cfg[conn_idx] = cccd_value;
367             break;
368 
369         case THS_IDX_SETTINGS_CFG:
370             s_ths_env.settings_ntf_cfg[conn_idx] = cccd_value;
371             break;
372 
373         default:
374             break;
375     }
376 }
377 
378 /**
379  *****************************************************************************************
380  * @brief Handles reception of the complete event.
381  *
382  * @param[in] conn_idx: Connection index.
383  * @param[in] p_param:  Pointer to the parameters of the complete event.
384  *****************************************************************************************
385  */
ths_gatts_cmpl_cb(uint8_t conn_idx,uint8_t status,const ble_gatts_ntf_ind_t * p_ntf_ind)386 static void ths_gatts_cmpl_cb(uint8_t conn_idx, uint8_t status, const ble_gatts_ntf_ind_t *p_ntf_ind)
387 {
388     if (s_ths_env.ths_init.evt_handler && SDK_SUCCESS == status) {
389         if (BLE_GATT_NOTIFICATION == p_ntf_ind->type && THS_IDX_TX_VAL == s_noti_cfg_idx) {
390             ths_evt_t event;
391             event.conn_idx  = conn_idx;
392             event.evt_type  = THS_EVT_DATA_SENT;
393             s_ths_env.ths_init.evt_handler(&event);
394         }
395     }
396 }
397 
398 /*
399  * GLOBAL FUNCTION DEFINITIONS
400  *****************************************************************************************
401  */
ths_data_send(uint8_t conn_idx,uint8_t * p_data,uint16_t length)402 sdk_err_t ths_data_send(uint8_t conn_idx, uint8_t *p_data, uint16_t length)
403 {
404     sdk_err_t   error_code = SDK_ERR_NTF_DISABLED;
405 
406     if (PRF_CLI_START_NTF == s_ths_env.data_ntf_cfg[conn_idx]) {
407         gatts_noti_ind_t send_cmd;
408 
409         // Fill in the parameter structure
410         send_cmd.type   = BLE_GATT_NOTIFICATION;
411         send_cmd.handle = prf_find_handle_by_idx(THS_IDX_TX_VAL, s_ths_env.start_hdl, (uint8_t *)&s_char_mask);
412         // Pack measured value in database
413         send_cmd.length = length;
414         send_cmd.value  = p_data;
415         // Send notification to peer device
416         error_code      = ble_gatts_noti_ind(conn_idx, &send_cmd);
417         s_noti_cfg_idx  = THS_IDX_TX_VAL;
418     }
419 
420     return error_code;
421 }
422 
ths_settings_notify(uint8_t conn_idx,uint8_t * p_settings,uint16_t length)423 sdk_err_t ths_settings_notify(uint8_t conn_idx, uint8_t *p_settings, uint16_t length)
424 {
425     sdk_err_t   error_code = SDK_ERR_NTF_DISABLED;
426 
427     if (PRF_CLI_START_NTF == s_ths_env.settings_ntf_cfg[conn_idx]) {
428         gatts_noti_ind_t send_cmd;
429 
430         // Fill in the parameter structure
431         send_cmd.type   = BLE_GATT_NOTIFICATION;
432         send_cmd.handle = prf_find_handle_by_idx(THS_IDX_SETTINGS_VAL, s_ths_env.start_hdl, (uint8_t *)&s_char_mask);
433 
434         // Pack measured value in database
435         send_cmd.length = length;
436         send_cmd.value  = p_settings;
437 
438         // Send notification to peer device
439         error_code       = ble_gatts_noti_ind(conn_idx, &send_cmd);
440         s_noti_cfg_idx  = THS_IDX_SETTINGS_VAL;
441     }
442 
443     return error_code;
444 }
445 
ths_transport_mode_get(void)446 ths_transport_mode_t ths_transport_mode_get(void)
447 {
448     return s_ths_env.ths_init.transport_mode;
449 }
450 
ths_service_init(ths_init_t * p_ths_init)451 sdk_err_t ths_service_init(ths_init_t *p_ths_init)
452 {
453     sdk_err_t ret;
454     if (p_ths_init == NULL) {
455         return SDK_ERR_POINTER_NULL;
456     }
457 
458     ret = memcpy_s(&s_ths_env.ths_init, sizeof(ths_init_t), p_ths_init, sizeof(ths_init_t));
459     if (ret < 0) {
460         return ret;
461     }
462 
463     return ble_server_prf_add(&ths_prf_info);
464 }
465 
466