1 /**
2 *****************************************************************************************
3 *
4 * @file ags.c
5 *
6 * @brief Alexa Gadget Profile 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 "ags.h"
43 #include "user_config.h"
44 #include "ble_prf_types.h"
45 #include "ble_prf_utils.h"
46 #include "utility.h"
47 #include "app_log.h"
48 #include "app_error.h"
49 #define MAX_PACKET_SIZE_OFFSET 3
50 #define INDEX_0 0
51 #define INDEX_1 1
52 #define INDEX_2 2
53 /*
54 * DEFINES
55 *****************************************************************************************
56 */
57 /**@brief The UUIDs of AGS characteristics.
58 * The Tx channel transmits data from an Echo device to a gadget.
59 * The Rx channel reansmits data from a gadget to an Echo device.
60 */
61 #define AGS_TX_UUID {0x76, 0x30, 0xF8, 0xDD, 0x90, 0xA3, 0x61, 0xAC, 0xA7, 0x43, 0x05, 0x30, 0x77, 0xB1, 0x4E, 0xF0}
62 #define AGS_RX_UUID {0x0B, 0x42, 0x82, 0x1F, 0x64, 0x72, 0x2F, 0x8A, 0xB4, 0x4B, 0x79, 0x18, 0x5B, 0xA0, 0xEE, 0x2B}
63
64 /**@brief Macros for conversion of 128bit to 16bit UUID. */
65 #define ATT_128_PRIMARY_SERVICE BLE_ATT_16_TO_128_ARRAY(BLE_ATT_DECL_PRIMARY_SERVICE)
66 #define ATT_128_CHARACTERISTIC BLE_ATT_16_TO_128_ARRAY(BLE_ATT_DECL_CHARACTERISTIC)
67 #define ATT_128_CLIENT_CHAR_CFG BLE_ATT_16_TO_128_ARRAY(BLE_ATT_DESC_CLIENT_CHAR_CFG)
68
69
70 /*
71 * ENUMERATIONS
72 *****************************************************************************************
73 */
74 /**@brief Alexa Gadget Service Attributes Indexes. */
75 enum {
76 AGS_IDX_SVC,
77
78 AGS_IDX_TX_CHAR,
79 AGS_IDX_TX_VAL,
80
81 AGS_IDX_RX_CHAR,
82 AGS_IDX_RX_VAL,
83 AGS_IDX_RX_NTF_CFG,
84
85 AGS_IDX_NB
86 };
87
88 /*
89 * STRUCTURES
90 *****************************************************************************************
91 */
92 /**@brief Alexa Gadget Service environment variable. */
93 struct ags_env_t {
94 ags_init_t ags_init; /**< Alexa Gadget Service initialization variables. */
95 uint16_t start_hdl; /**< Alexa Gadget Service start handle. */
96 ags_stream_env_t ags_stream_env[3]; /**< The environment variable of gadget stream. */
97 uint16_t
98 rx_ntf_cfg[AGS_CONNECTION_MAX]; /**< The configuration of Rx Notification \
99 which is configured by the peer devices. */
100 };
101
102 /**@brief Alexa Gadget stream payload. */
103 typedef struct {
104 uint8_t p_data[USER_GADGET_TRANSACTION_BUF_SIZE]; /**< Gadget stream payload. */
105 uint16_t length; /**< Length of data. */
106 uint16_t offset; /**< Offset of data. */
107 } ags_stream_payload_t;
108
109 /*
110 * LOCAL FUNCTION DECLARATION
111 *****************************************************************************************
112 */
113 static sdk_err_t ags_init(void);
114 static void ags_read_att_cb(uint8_t conn_idx, const gatts_read_req_cb_t *p_param);
115 static void ags_write_att_cb(uint8_t conn_idx, const gatts_write_req_cb_t *p_param);
116 static void ags_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value);
117 static void ags_ntf_ind_cb(uint8_t conn_idx, uint8_t status, const ble_gatts_ntf_ind_t *p_ntf_ind);
118
119 static sdk_err_t ags_echo_rx_val_chunk(uint8_t conn_idx);
120 static sdk_err_t ags_stream_ack_send(uint8_t conn_idx, uint8_t stream_env_id, bool ack_flag);
121 static void ags_echo_tx_val_decode(uint8_t conn_idx, const uint8_t *p_data, uint16_t length);
122 static void ags_encode_ack(ags_ack_packet_t *p_ack_packet, uint8_t stream_id, uint8_t trxn_id, bool ack_flag);
123 static void ags_encode_header(uint8_t *header, uint8_t stream_id, uint8_t trxn_type, uint8_t payload_length,
124 uint16_t trxn_length);
125 static void ags_reset_stream(uint8_t stream_env_id, uint16_t total_length);
126
127 /*
128 * LOCAL VARIABLE DEFINITIONS
129 *****************************************************************************************
130 */
131 static struct ags_env_t s_ags_env;
132 static uint8_t s_gadget_tx_buffer[244];
133 static uint8_t s_trxn_counter;
134 static uint8_t s_tx_sequ_num;
135 static ags_stream_payload_t s_ags_stream_payload;
136
137 /**@brief Full AGS Database Description - Used to add attributes into the database. */
138 static const attm_desc_128_t ags_attr_tab[AGS_IDX_NB] = {
139 // Alexa Gadget Service Declaration
140 [AGS_IDX_SVC] = {ATT_128_PRIMARY_SERVICE, READ_PERM_UNSEC, 0, 0},
141
142 // AGS Tx Characteristic - Declaration
143 [AGS_IDX_TX_CHAR] = {ATT_128_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
144 // AGS Tx Characteristic - Value
145 [AGS_IDX_TX_VAL] = {
146 AGS_TX_UUID,
147 WRITE_REQ_PERM(UNAUTH) | WRITE_CMD_PERM(UNAUTH),
148 (ATT_VAL_LOC_USER | ATT_UUID_TYPE_SET(UUID_TYPE_128)),
149 AGS_TX_VAL_LEN_MAX
150 },
151
152 // AGS Tx Characteristic - Declaration
153 [AGS_IDX_RX_CHAR] = {ATT_128_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
154 // AGS Tx Characteristic - Value
155 [AGS_IDX_RX_VAL] = {
156 AGS_RX_UUID,
157 NOTIFY_PERM(UNAUTH),
158 (ATT_VAL_LOC_USER | ATT_UUID_TYPE_SET(UUID_TYPE_128)),
159 AGS_RX_VAL_LEN_MAX
160 },
161 // AGS Tx Characteristic - Client Characteristic Configuration Descriptor
162 [AGS_IDX_RX_NTF_CFG] = {
163 ATT_128_CLIENT_CHAR_CFG,
164 READ_PERM(UNAUTH) | WRITE_REQ_PERM(UNAUTH),
165 0,
166 0
167 },
168 };
169
170 /**@brief AGS Task interface required by profile manager. */
171 static ble_prf_manager_cbs_t ags_task_cbs = {
172 (prf_init_func_t) ags_init,
173 NULL,
174 NULL,
175 };
176
177 /**@brief AGS Task Callbacks. */
178 static gatts_prf_cbs_t ags_cb_func = {
179 ags_read_att_cb,
180 ags_write_att_cb,
181 NULL,
182 ags_ntf_ind_cb,
183 ags_cccd_set_cb
184 };
185
186 /**@brief AGS Information. */
187 static const prf_server_info_t ags_prf_info = {
188 .max_connection_nb = AGS_CONNECTION_MAX,
189 .manager_cbs = &ags_task_cbs,
190 .gatts_prf_cbs = &ags_cb_func,
191 };
192
193 /*
194 * LOCAL FUNCTION DEFINITIONS
195 *****************************************************************************************
196 */
197 /**
198 *****************************************************************************************
199 * @brief Initialize Alexa Gadget service create db in att
200 *
201 * @return Error code to know if profile initialization succeed or not.
202 *****************************************************************************************
203 */
ags_init(void)204 static sdk_err_t ags_init(void)
205 {
206 // The start hanlde must be set with PRF_INVALID_HANDLE to be allocated automatically by BLE Stack.
207 uint16_t start_hdl = PRF_INVALID_HANDLE;
208 const uint8_t ags_svc_uuid[] = {AGS_SERVICE_UUID};
209 sdk_err_t error_code;
210 gatts_create_db_t gatts_db;
211
212 error_code = memset_s(&gatts_db, sizeof(gatts_db), 0, sizeof(gatts_db));
213 if (error_code < 0) {
214 return error_code;
215 }
216
217 gatts_db.shdl = &start_hdl;
218 gatts_db.uuid = ags_svc_uuid;
219 gatts_db.attr_tab_cfg = (uint8_t *)&(s_ags_env.ags_init.char_mask);
220 gatts_db.max_nb_attr = AGS_IDX_NB;
221 gatts_db.srvc_perm = SRVC_UUID_TYPE_SET(UUID_TYPE_128);
222 gatts_db.attr_tab_type = SERVICE_TABLE_TYPE_128;
223 gatts_db.attr_tab.attr_tab_128 = ags_attr_tab;
224
225 error_code = ble_gatts_srvc_db_create(&gatts_db);
226 if (SDK_SUCCESS == error_code) {
227 s_ags_env.start_hdl = *gatts_db.shdl;
228 }
229
230 return error_code;
231 }
232
233 /**
234 *****************************************************************************************
235 * @brief Handles reception of the attribute info request message.
236 *
237 * @param[in] conn_idx: Connection index
238 * @param[in] p_param: The parameters of the read request.
239 *****************************************************************************************
240 */
ags_read_att_cb(uint8_t conn_idx,const gatts_read_req_cb_t * p_param)241 static void ags_read_att_cb(uint8_t conn_idx, const gatts_read_req_cb_t *p_param)
242 {
243 gatts_read_cfm_t cfm;
244 uint8_t handle = p_param->handle;
245 uint8_t tab_index = prf_find_idx_by_handle(handle,
246 s_ags_env.start_hdl,
247 AGS_IDX_NB,
248 (uint8_t *)&s_ags_env.ags_init.char_mask);
249 cfm.handle = handle;
250 cfm.status = BLE_SUCCESS;
251
252 switch (tab_index) {
253 case AGS_IDX_RX_NTF_CFG:
254 cfm.length = sizeof(uint16_t);
255 cfm.value = (uint8_t *)&s_ags_env.rx_ntf_cfg[conn_idx];
256 break;
257
258 default:
259 cfm.length = 0;
260 cfm.status = BLE_ATT_ERR_INVALID_HANDLE;
261 break;
262 }
263
264 ble_gatts_read_cfm(conn_idx, &cfm);
265 }
266
267 /**
268 *****************************************************************************************
269 * @brief Handles reception of the write request.
270 *
271 * @param[in]: conn_idx: Connection index
272 * @param[in]: p_param: The parameters of the write request.
273 *****************************************************************************************
274 */
ags_write_att_cb(uint8_t conn_idx,const gatts_write_req_cb_t * p_param)275 static void ags_write_att_cb(uint8_t conn_idx, const gatts_write_req_cb_t *p_param)
276 {
277 uint16_t handle = p_param->handle;
278 uint16_t tab_index = 0;
279 uint16_t cccd_value = 0;
280 ags_evt_t event;
281 gatts_write_cfm_t cfm;
282
283 tab_index = prf_find_idx_by_handle(handle,
284 s_ags_env.start_hdl,
285 AGS_IDX_NB,
286 (uint8_t *)&s_ags_env.ags_init.char_mask);
287 cfm.handle = handle;
288 cfm.status = BLE_SUCCESS;
289 event.evt_type = AGS_EVT_INVALID;
290 event.conn_idx = conn_idx;
291
292 switch (tab_index) {
293 case AGS_IDX_TX_VAL:
294 ags_echo_tx_val_decode(conn_idx, p_param->value, p_param->length);
295 event.evt_type = AGS_EVT_ECHO_TX_DATA_RECEIVED;
296 break;
297
298 case AGS_IDX_RX_NTF_CFG:
299 cccd_value = le16toh(&p_param->value[0]);
300 event.evt_type = ((PRF_CLI_START_NTF == cccd_value) ?\
301 AGS_EVT_ECHO_RX_NOTI_ENABLE :\
302 AGS_EVT_ECHO_RX_NOTI_DISABLE);
303 s_ags_env.rx_ntf_cfg[conn_idx] = cccd_value;
304 break;
305
306 default:
307 cfm.status = BLE_ATT_ERR_INVALID_HANDLE;
308 break;
309 }
310
311 ble_gatts_write_cfm(conn_idx, &cfm);
312
313 if (BLE_ATT_ERR_INVALID_HANDLE != cfm.status &&
314 AGS_EVT_INVALID != event.evt_type && s_ags_env.ags_init.evt_handler) {
315 s_ags_env.ags_init.evt_handler(&event);
316 }
317 }
318
319 /**
320 *****************************************************************************************
321 * @brief Handles reception of the cccd recover request.
322 *
323 * @param[in]: conn_idx: Connection index
324 * @param[in]: handle: The handle of cccd attribute.
325 * @param[in]: cccd_value: The value of cccd attribute.
326 *****************************************************************************************
327 */
ags_cccd_set_cb(uint8_t conn_idx,uint16_t handle,uint16_t cccd_value)328 static void ags_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value)
329 {
330 uint16_t tab_index = 0;
331 ags_evt_t event;
332
333 if (!prf_is_cccd_value_valid(cccd_value)) {
334 return;
335 }
336
337 tab_index = prf_find_idx_by_handle(handle,
338 s_ags_env.start_hdl,
339 AGS_IDX_NB,
340 (uint8_t *)&s_ags_env.ags_init.char_mask);
341
342 event.evt_type = AGS_EVT_INVALID;
343 event.conn_idx = conn_idx;
344
345 switch (tab_index) {
346 case AGS_IDX_RX_NTF_CFG:
347 event.evt_type = ((PRF_CLI_START_NTF == cccd_value) ?\
348 AGS_EVT_ECHO_RX_NOTI_ENABLE :\
349 AGS_EVT_ECHO_RX_NOTI_DISABLE);
350 s_ags_env.rx_ntf_cfg[conn_idx] = cccd_value;
351 break;
352
353 default:
354 break;
355 }
356
357 if (AGS_EVT_INVALID != event.evt_type && s_ags_env.ags_init.evt_handler) {
358 s_ags_env.ags_init.evt_handler(&event);
359 }
360 }
361
362 /**
363 *****************************************************************************************
364 * @brief Handles reception of the complete event.
365 *
366 * @param[in] conn_idx: Connection index.
367 * @param[in] status: The status of the complete event.
368 * @param[in] p_ntf_id: Pointer to the parameters of the complete event.
369 *****************************************************************************************
370 */
ags_ntf_ind_cb(uint8_t conn_idx,uint8_t status,const ble_gatts_ntf_ind_t * p_ntf_ind)371 static void ags_ntf_ind_cb(uint8_t conn_idx, uint8_t status, const ble_gatts_ntf_ind_t *p_ntf_ind)
372 {
373 if (s_ags_env.ags_init.evt_handler && SDK_SUCCESS == status) {
374 if (BLE_GATT_NOTIFICATION == p_ntf_ind->type) {
375 ags_echo_rx_val_chunk(conn_idx);
376 }
377 }
378 }
379
380 /**
381 *****************************************************************************************
382 * @brief Reset the gadget stream environment variables.
383 *
384 * @param[in] stream_env_id: The environment ID of gadget stream which is active.
385 * @param[in] total_length: The total payload length of this stream.
386 *****************************************************************************************
387 */
ags_reset_stream(uint8_t stream_env_id,uint16_t total_length)388 static inline void ags_reset_stream(uint8_t stream_env_id, uint16_t total_length)
389 {
390 s_ags_env.ags_stream_env[stream_env_id].is_active = true;
391 s_ags_env.ags_stream_env[stream_env_id].ack_flag = false;
392 s_ags_env.ags_stream_env[stream_env_id].trxn_id = 0;
393 s_ags_env.ags_stream_env[stream_env_id].total_length = total_length;
394 s_ags_env.ags_stream_env[stream_env_id].received_length = 0;
395 }
396
397 /**
398 *****************************************************************************************
399 * @brief Decode the data from Echo device.
400 *
401 * @param[in] conn_idx: Connection index.
402 * @param[in] p_data: Pointer to the data which to be decoded.
403 * @param[in] length: Data length.
404 *****************************************************************************************
405 */
ags_echo_tx_val_decode(uint8_t conn_idx,const uint8_t * p_data,uint16_t length)406 static void ags_echo_tx_val_decode(uint8_t conn_idx, const uint8_t *p_data, uint16_t length)
407 {
408 sdk_err_t error_code;
409 uint8_t stream_env_id;
410 uint16_t payload_length;
411 uint16_t payload_offset;
412
413 if (p_data == NULL || (length < sizeof(ags_header_base_t))) {
414 APP_LOG_DEBUG("Unexcepted length: %d", length);
415 return;
416 }
417
418 ags_header_base_t *p_header_base = (ags_header_base_t *)p_data;
419
420 for (uint8_t i = 0; i < ARRAY_SIZE(s_ags_env.ags_stream_env); i++) {
421 if (s_ags_env.ags_stream_env[i].stream_id == p_header_base->stream_id) {
422 stream_env_id = i;
423 }
424 }
425
426 if (s_ags_env.ags_stream_env[stream_env_id].is_active) {
427 if (p_header_base->length_ext) {
428 ags_header_subs_ext_t *p_header_subs_ext = (ags_header_subs_ext_t *)p_data;
429
430 payload_length = be16toh(p_header_subs_ext->payload_length);
431 payload_offset = sizeof(ags_header_subs_ext_t);
432 } else {
433 ags_header_subs_t *p_header_subs = (ags_header_subs_t *)p_data;
434
435 payload_length = p_header_subs->payload_length;
436 payload_offset = sizeof(ags_header_subs_t);
437 }
438 } else {
439 uint16_t total_length;
440
441 if (p_header_base->length_ext) {
442 ags_header_first_ext_t *p_header_first_ext = (ags_header_first_ext_t *)p_data;
443
444 total_length = be16toh(p_header_first_ext->total_trxn_length);
445 payload_length = be16toh(p_header_first_ext->payload_length);
446 payload_offset = sizeof(ags_header_first_ext_t);
447 } else {
448 ags_header_first_t *p_header_first = (ags_header_first_t *)p_data;
449
450 total_length = be16toh(p_header_first->total_trxn_length);
451 payload_length = p_header_first->payload_length;
452 payload_offset = sizeof(ags_header_first_t);
453 }
454
455 ags_reset_stream(stream_env_id, total_length);
456 }
457
458 if (p_header_base->ack_flag) {
459 s_ags_env.ags_stream_env[stream_env_id].ack_flag = p_header_base->ack_flag;
460 s_ags_env.ags_stream_env[stream_env_id].trxn_id = p_header_base->trxn_id;
461 }
462
463 s_ags_env.ags_stream_env[stream_env_id].received_length += payload_length;
464
465 if (s_ags_env.ags_stream_env[stream_env_id].received_length == \
466 s_ags_env.ags_stream_env[stream_env_id].total_length) {
467 s_ags_env.ags_stream_env[stream_env_id].is_active = 0;
468 } else if (s_ags_env.ags_stream_env[stream_env_id].received_length >
469 s_ags_env.ags_stream_env[stream_env_id].total_length) {
470 s_ags_env.ags_stream_env[stream_env_id].is_active = 0;
471 APP_LOG_DEBUG("Stream Overrun: %d %d.", s_ags_env.ags_stream_env[stream_env_id].received_length,
472 s_ags_env.ags_stream_env[stream_env_id].total_length);
473 }
474
475 bool ack_flag = false;
476
477 switch (p_header_base->stream_id) {
478 case AGS_CONTROL_STREAM_ID:
479 ack_flag = s_ags_env.ags_init.ags_control_stream_cb(conn_idx, &((uint8_t *)p_data)[payload_offset], \
480 payload_length, s_ags_env.ags_stream_env[stream_env_id].is_active);
481 break;
482
483 case AGS_ALEXA_STREAM_ID:
484 ack_flag = s_ags_env.ags_init.ags_alexa_stream_cb(conn_idx, &((uint8_t *)p_data)[payload_offset], \
485 payload_length, s_ags_env.ags_stream_env[stream_env_id].is_active);
486 break;
487
488 default:
489 APP_LOG_DEBUG("Unknown Stream ID: %d", p_header_base->stream_id);
490 break;
491 }
492
493 error_code = ags_stream_ack_send(conn_idx, stream_env_id, ack_flag);
494 APP_ERROR_CHECK(error_code);
495 return;
496 }
497
498 /**
499 *****************************************************************************************
500 * @brief Send ACK packet.
501 * If the Echo device request the ACK packet, the gadget must send the Echo device
502 * an ACK packet in response to the last packet of the transaction.
503 * @param[in] conn_idx: Connection index.
504 * @param[in] stream_env_id: The environment ID of gadget stream which is active.
505 * @param[in] ack_flag: The ack flag of the ACK packet.
506 *
507 * @return the result of sending ACK packet.
508 *****************************************************************************************
509 */
ags_stream_ack_send(uint8_t conn_idx,uint8_t stream_env_id,bool ack_flag)510 static sdk_err_t ags_stream_ack_send(uint8_t conn_idx, uint8_t stream_env_id, bool ack_flag)
511 {
512 sdk_err_t error_code = SDK_SUCCESS;
513 ags_evt_t ags_evt;
514
515 if (!s_ags_env.ags_stream_env[stream_env_id].is_active && (s_ags_env.ags_stream_env[stream_env_id].ack_flag)) {
516 ags_ack_packet_t ack_packet;
517
518 ags_encode_ack(&ack_packet, s_ags_env.ags_stream_env[stream_env_id].stream_id, \
519 s_ags_env.ags_stream_env[stream_env_id].trxn_id, ack_flag);
520
521 gatts_noti_ind_t ags_rx_val_noti;
522
523 ags_rx_val_noti.type = BLE_GATT_NOTIFICATION;
524 ags_rx_val_noti.handle = prf_find_handle_by_idx(AGS_IDX_RX_VAL,
525 s_ags_env.start_hdl,
526 (uint8_t *)&s_ags_env.ags_init.char_mask);
527 ags_rx_val_noti.length = sizeof(ack_packet);
528 ags_rx_val_noti.value = (uint8_t *)&ack_packet;
529
530 error_code = ble_gatts_noti_ind(conn_idx, &ags_rx_val_noti);
531
532 ags_evt.evt_type = AGS_EVT_ECHO_RX_DATA_SENT;
533 ags_evt.conn_idx = conn_idx;
534 ags_evt.p_data = (uint8_t *)&ack_packet;
535 ags_evt.length = sizeof(ack_packet);
536 s_ags_env.ags_init.evt_handler(&ags_evt);
537 }
538 return error_code;
539 }
540
541 /**
542 *****************************************************************************************
543 * @brief Send the data to RX Characteristic chunk by chunk.
544 *
545 * @param[in] conn_idx: Connection index.
546 *
547 * @return the result of sending operation.
548 *****************************************************************************************
549 */
ags_echo_rx_val_chunk(uint8_t conn_idx)550 static sdk_err_t ags_echo_rx_val_chunk(uint8_t conn_idx)
551 {
552 sdk_err_t error_code;
553 ags_evt_t ags_evt;
554
555 if (s_ags_stream_payload.length) {
556 uint8_t header_size = sizeof(ags_header_subs_t);
557 uint16_t max_packet_size;
558 uint16_t payload_size;
559 uint8_t stream_id;
560 uint8_t trxn_type;
561 gatts_noti_ind_t ags_rx_val_noti;
562
563 ble_gatt_mtu_get(conn_idx, &max_packet_size);
564 max_packet_size -= MAX_PACKET_SIZE_OFFSET;
565
566 if (max_packet_size < (header_size + s_ags_stream_payload.length)) {
567 payload_size = max_packet_size - header_size;
568 s_ags_stream_payload.length -= payload_size;
569 } else {
570 payload_size = s_ags_stream_payload.length;
571 s_ags_stream_payload.length = 0;
572 }
573
574 // Get stream ID from prededing transmit.
575 stream_id = ((ags_header_base_t *)s_gadget_tx_buffer)->stream_id;
576 if (s_ags_stream_payload.length) {
577 trxn_type = AGS_TRANSACTION_TYPE_CONT;
578 } else {
579 trxn_type = AGS_TRANSACTION_TYPE_LAST;
580 }
581
582 ags_encode_header(&s_gadget_tx_buffer[0], stream_id, trxn_type, payload_size, 0);
583 error_code = memcpy_s(&s_gadget_tx_buffer[header_size], payload_size,
584 &s_ags_stream_payload.p_data[s_ags_stream_payload.offset], payload_size);
585 s_ags_stream_payload.offset += payload_size;
586
587 ags_rx_val_noti.type = BLE_GATT_NOTIFICATION;
588 ags_rx_val_noti.handle = prf_find_handle_by_idx(AGS_IDX_RX_VAL,
589 s_ags_env.start_hdl,
590 (uint8_t *)&s_ags_env.ags_init.char_mask);
591 ags_rx_val_noti.length = header_size + payload_size;
592 ags_rx_val_noti.value = s_gadget_tx_buffer;
593
594 error_code = ble_gatts_noti_ind(conn_idx, &ags_rx_val_noti);
595 } else {
596 error_code = memset_s(&s_ags_stream_payload, sizeof(ags_stream_payload_t), 0, sizeof(ags_stream_payload_t));
597 if (error_code < 0) {
598 return error_code;
599 }
600 s_ags_stream_payload.length = 0;
601 s_ags_stream_payload.offset = 0;
602
603 ags_evt.evt_type = AGS_EVT_ECHO_RX_DATA_SENT;
604 ags_evt.conn_idx = conn_idx;
605 ags_evt.p_data = s_ags_stream_payload.p_data;
606 s_ags_env.ags_init.evt_handler(&ags_evt);
607
608 error_code = SDK_SUCCESS;
609 }
610 return error_code;
611 }
612
613 /**
614 *****************************************************************************************
615 * @brief Enocde the ACK packet header.
616 *
617 * @param[in] p_ack_packet: Pointer to the ACK packet.
618 * @param[in] stream_id: The stream ID of the packet that requested the acknowledgement.
619 * @param[in] trxn_id: The tracsaction ID of the packet that requested the acknowledgement.
620 * @param[in] ack_flag: The ack flag of the ACK packet.
621 *****************************************************************************************
622 */
ags_encode_ack(ags_ack_packet_t * p_ack_packet,uint8_t stream_id,uint8_t trxn_id,bool ack_flag)623 static void ags_encode_ack(ags_ack_packet_t *p_ack_packet, uint8_t stream_id, uint8_t trxn_id, bool ack_flag)
624 {
625 p_ack_packet->header_base.stream_id = stream_id;
626 p_ack_packet->header_base.trxn_id = trxn_id;
627 p_ack_packet->header_base.sequ_num = 0; // This can be any value.
628 p_ack_packet->header_base.trxn_type = AGS_TRANSACTION_TYPE_CTRL;
629 p_ack_packet->header_base.ack_flag = ack_flag ? AGS_ACK_ACK : AGS_ACK_NACK;
630 p_ack_packet->header_base.length_ext = AGS_LEN_EXT_NO_EXT;
631 p_ack_packet->reserved_1 = AGS_HEADER_ACK_RES_1;
632 p_ack_packet->payload_length = AGS_HEADER_ACK_PAYLOAD_LEN;
633 p_ack_packet->reserved_2 = AGS_HEADER_ACK_RES_2;
634 p_ack_packet->result_code = ack_flag ? AGS_RES_CODE_SUCCESS : AGS_RES_CODE_UNSUPPORTED;
635 }
636
637 /**
638 *****************************************************************************************
639 * @brief Enocde the header of other type packet.
640 *
641 * @param[in] p_header: Pointer to the header.
642 * @param[in] stream_id: The stream ID, @ref ags_header_stream_id_t.
643 * @param[in] trxn_type: The transaction type, @ref ags_header_trxn_type_t.
644 * @param[in] payload_length: Payload length.
645 * @param[in] trxn_length: Total transaction length.
646 *****************************************************************************************
647 */
ags_encode_header(uint8_t * header,uint8_t stream_id,uint8_t trxn_type,uint8_t payload_length,uint16_t trxn_length)648 static void ags_encode_header(uint8_t *header, uint8_t stream_id, uint8_t trxn_type, uint8_t payload_length,
649 uint16_t trxn_length)
650 {
651 ags_header_base_t *p_header_base = header;
652
653 p_header_base->stream_id = stream_id;
654 p_header_base->sequ_num = s_tx_sequ_num++;
655 p_header_base->trxn_type = trxn_type;
656 p_header_base->ack_flag = AGS_ACK_NACK;
657 p_header_base->length_ext = AGS_LEN_EXT_NO_EXT;
658
659 if (AGS_TRANSACTION_TYPE_FIRST == trxn_type) {
660 ags_header_first_t *p_header_first = header;
661
662 s_trxn_counter++;
663 p_header_base->trxn_id = s_trxn_counter;
664 p_header_first->reserved = AGS_HEADER_FIRST_RES;
665 p_header_first->payload_length = payload_length;
666
667 htobe16(&(p_header_first->total_trxn_length), trxn_length);
668 } else {
669 ags_header_subs_t *p_header_subs = header;
670
671 p_header_base->trxn_id = s_trxn_counter;
672 p_header_subs->payload_length = payload_length;
673 }
674 }
675
676 /*
677 * GLOBAL FUNCTION DEFINITIONS
678 ****************************************************************************************
679 */
ags_stream_send(uint8_t conn_idx,uint8_t stream_id,uint8_t * p_data,uint16_t length)680 sdk_err_t ags_stream_send(uint8_t conn_idx, uint8_t stream_id, uint8_t *p_data, uint16_t length)
681 {
682 sdk_err_t error_code = SDK_ERR_NTF_DISABLED;
683 uint16_t max_packet_size;
684 uint16_t payload_size;
685 uint16_t header_size;
686 gatts_noti_ind_t ags_rx_val_noti;
687
688 error_code = memset_s(&s_ags_stream_payload, sizeof(s_ags_stream_payload),
689 0, sizeof(s_ags_stream_payload));
690 if (error_code < 0) {
691 return error_code;
692 }
693 s_tx_sequ_num = 0;
694 s_ags_stream_payload.length = 0;
695
696 ble_gatt_mtu_get(conn_idx, &max_packet_size);
697 max_packet_size -= MAX_PACKET_SIZE_OFFSET;
698
699 error_code = memcpy_s(s_ags_stream_payload.p_data, length, (uint8_t *)p_data, length);
700 if (error_code < 0) {
701 return error_code;
702 }
703
704 header_size = sizeof(ags_header_first_t);
705
706 if (max_packet_size < (header_size + length)) {
707 s_ags_stream_payload.length = header_size + length - max_packet_size;
708 } else {
709 s_ags_stream_payload.length = 0;
710 }
711
712 payload_size = length - s_ags_stream_payload.length;
713 s_ags_stream_payload.offset = payload_size;
714
715 ags_encode_header(&s_gadget_tx_buffer[0], stream_id, AGS_TRANSACTION_TYPE_FIRST, payload_size, length);
716 error_code = memcpy_s(&s_gadget_tx_buffer[header_size], payload_size, p_data, payload_size);
717 if (error_code < 0) {
718 return error_code;
719 }
720
721 ags_rx_val_noti.type = BLE_GATT_NOTIFICATION;
722 ags_rx_val_noti.handle = prf_find_handle_by_idx(AGS_IDX_RX_VAL,
723 s_ags_env.start_hdl,
724 (uint8_t *)&s_ags_env.ags_init.char_mask);
725 ags_rx_val_noti.length = header_size + payload_size;
726 ags_rx_val_noti.value = s_gadget_tx_buffer;
727
728 error_code = ble_gatts_noti_ind(conn_idx, &ags_rx_val_noti);
729 return error_code;
730 }
731
ags_non_stream_send(uint8_t conn_idx,uint8_t * p_data,uint16_t length)732 sdk_err_t ags_non_stream_send(uint8_t conn_idx, uint8_t *p_data, uint16_t length)
733 {
734 sdk_err_t error_code = SDK_ERR_NTF_DISABLED;
735 gatts_noti_ind_t ags_rx_val_noti;
736 ags_evt_t ags_evt;
737
738 ags_rx_val_noti.type = BLE_GATT_NOTIFICATION;
739 ags_rx_val_noti.handle = prf_find_handle_by_idx(AGS_IDX_RX_VAL,
740 s_ags_env.start_hdl,
741 (uint8_t *)&s_ags_env.ags_init.char_mask);
742 ags_rx_val_noti.length = length;
743 ags_rx_val_noti.value = (uint8_t *)p_data;
744
745 error_code = ble_gatts_noti_ind(conn_idx, &ags_rx_val_noti);
746
747 ags_evt.evt_type = AGS_EVT_ECHO_RX_DATA_SENT;
748 ags_evt.conn_idx = conn_idx;
749 ags_evt.p_data = (uint8_t *)p_data;
750 ags_evt.length = length;
751 s_ags_env.ags_init.evt_handler(&ags_evt);
752
753 return error_code;
754 }
755
ags_service_init(ags_init_t * p_ags_init)756 sdk_err_t ags_service_init(ags_init_t *p_ags_init)
757 {
758 sdk_err_t ret;
759 if (p_ags_init == NULL) {
760 return SDK_ERR_POINTER_NULL;
761 }
762
763 s_ags_env.ags_stream_env[INDEX_0].stream_id = AGS_CONTROL_STREAM_ID;
764 s_ags_env.ags_stream_env[INDEX_0].is_active = false;
765 s_ags_env.ags_stream_env[INDEX_1].stream_id = AGS_ALEXA_STREAM_ID;
766 s_ags_env.ags_stream_env[INDEX_1].is_active = false;
767 s_ags_env.ags_stream_env[INDEX_2].stream_id = AGS_OTA_STREAM_ID;
768 s_ags_env.ags_stream_env[INDEX_2].is_active = false;
769
770 ret = memcpy_s(&s_ags_env.ags_init, sizeof(ags_init_t), p_ags_init, sizeof(ags_init_t));
771 if (ret < 0) {
772 return ret;
773 }
774 return ble_server_prf_add(&ags_prf_info);
775 }
776