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