1 /** 2 ***************************************************************************************** 3 * 4 * @file mlmr.c 5 * 6 * @brief Goodix UART 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 "mlmr.h" 43 #include "ble_prf_types.h" 44 #include "ble_prf_utils.h" 45 #include "utility.h" 46 #include "user_app.h" 47 #include "app_log.h" 48 #define MTU_DEF_OFFSET 3 49 #define DATA_COPY_LEN 2 50 /* 51 * DEFINES 52 ***************************************************************************************** 53 */ 54 /**@brief The UUIDs of GUS characteristics. */ 55 #define GUS_SERVER_TX_UUID {0x1B, 0xD7, 0x90, 0xEC, 0xE8, 0xB9, 0x75, 56 0x80, 0x0A, 0x46, 0x44, 0xD3, 0x02, 0x02, 0xED, 0xA6} 57 #define GUS_SERVER_RX_UUID {0x1B, 0xD7, 0x90, 0xEC, 0xE8, 0xB9, 0x75, 58 0x80, 0x0A, 0x46, 0x44, 0xD3, 0x03, 0x02, 0xED, 0xA6} 59 #define GUS_FLOW_CTRL_UUID {0x1B, 0xD7, 0x90, 0xEC, 0xE8, 0xB9, 0x75, 60 0x80, 0x0A, 0x46, 0x44, 0xD3, 0x04, 0x02, 0xED, 0xA6} 61 62 /**@brief Macros for conversion of 128bit to 16bit UUID. */ 63 #define ATT_128_PRIMARY_SERVICE BLE_ATT_16_TO_128_ARRAY(BLE_ATT_DECL_PRIMARY_SERVICE) 64 #define ATT_128_CHARACTERISTIC BLE_ATT_16_TO_128_ARRAY(BLE_ATT_DECL_CHARACTERISTIC) 65 #define ATT_128_CLIENT_CHAR_CFG BLE_ATT_16_TO_128_ARRAY(BLE_ATT_DESC_CLIENT_CHAR_CFG) 66 67 /* 68 * ENUMERATIONS 69 ***************************************************************************************** 70 */ 71 /**@brief Goodix UART Service Attributes Indexes. */ 72 enum gus_attr_idx_t { 73 GUS_IDX_SVC, 74 75 GUS_IDX_TX_CHAR, 76 GUS_IDX_TX_VAL, 77 GUS_IDX_TX_CFG, 78 79 GUS_IDX_RX_CHAR, 80 GUS_IDX_RX_VAL, 81 82 GUS_IDX_FLOW_CTRL_CHAR, 83 GUS_IDX_FLOW_CTRL_VAL, 84 GUS_IDX_FLOW_CTRL_CFG, 85 86 GUS_IDX_NB, 87 }; 88 89 /* 90 * STRUCTURES 91 ***************************************************************************************** 92 */ 93 /**@brief Goodix UART Service environment variable. */ 94 struct gus_env_t { 95 gus_init_t gus_init; /**< Goodix UART Service initialization variables. */ 96 uint16_t start_hdl; /**< Start handle of services */ 97 uint16_t tx_ntf_cfg[GUS_CONNECTION_MAX]; /**< TX Characteristic Notification configuration of 98 the peers. */ 99 uint16_t 100 flow_ctrl_ntf_cfg[GUS_CONNECTION_MAX]; /**< Flow Control Characteristic Notification configuration of the peers. */ 101 }; 102 103 /* 104 * LOCAL FUNCTION DECLARATION 105 ***************************************************************************************** 106 */ 107 static sdk_err_t gus_init(void); 108 static void gus_write_att_cb(uint8_t conn_idx, const gatts_write_req_cb_t *p_param); 109 static void gus_read_att_cb(uint8_t conn_idx, const gatts_read_req_cb_t *p_param); 110 static void gus_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value); 111 static void gus_ntf_ind_cb(uint8_t conn_idx, uint8_t status, const ble_gatts_ntf_ind_t *p_ntf_ind); 112 113 /* 114 * LOCAL VARIABLE DEFINITIONS 115 ***************************************************************************************** 116 */ 117 static struct gus_env_t s_gus_env; 118 static const uint16_t s_char_mask = 0x003F; 119 120 static uint16_t received_data_len[CFG_BOND_DEVS]; 121 static uint16_t send_data_len[CFG_BOND_DEVS]; 122 static uint8_t adv_header = 0xA0; 123 static uint8_t rx_buffer[CFG_BOND_DEVS][516]; 124 125 /**@brief Full GUS Database Description which is used to add attributes into the ATT database. */ 126 static const attm_desc_128_t gus_att_db[GUS_IDX_NB] = { 127 // GUS service 128 [GUS_IDX_SVC] = {ATT_128_PRIMARY_SERVICE, READ_PERM_UNSEC, 0, 0}, 129 130 // GUS TX Characteristic Declaration 131 [GUS_IDX_TX_CHAR] = {ATT_128_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0}, 132 // GUS TX Characteristic Value 133 [GUS_IDX_TX_VAL] = { 134 GUS_SERVER_TX_UUID, 135 NOTIFY_PERM_UNSEC, 136 (ATT_VAL_LOC_USER | ATT_UUID_TYPE_SET(UUID_TYPE_128)), 137 GUS_MAX_DATA_LEN 138 }, 139 // GUS TX Characteristic - Client Characteristic Configuration Descriptor 140 [GUS_IDX_TX_CFG] = { 141 ATT_128_CLIENT_CHAR_CFG, 142 READ_PERM_UNSEC | WRITE_REQ_PERM_UNSEC, 143 0, 144 0 145 }, 146 147 // GUS RX Characteristic Declaration 148 [GUS_IDX_RX_CHAR] = {ATT_128_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0}, 149 // GUS RX Characteristic Value 150 [GUS_IDX_RX_VAL] = { 151 GUS_SERVER_RX_UUID, 152 WRITE_REQ_PERM_UNSEC | WRITE_CMD_PERM_UNSEC, 153 (ATT_VAL_LOC_USER | ATT_UUID_TYPE_SET(UUID_TYPE_128)), 154 GUS_MAX_DATA_LEN 155 }, 156 157 // GUS FLOW_CTRL Characteristic Declaration 158 [GUS_IDX_FLOW_CTRL_CHAR] = {ATT_128_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0}, 159 // GUS FLOW_CTRL Characteristic Value 160 [GUS_IDX_FLOW_CTRL_VAL] = { 161 GUS_FLOW_CTRL_UUID, 162 NOTIFY_PERM_UNSEC | WRITE_REQ_PERM_UNSEC, 163 (ATT_VAL_LOC_USER | ATT_UUID_TYPE_SET(UUID_TYPE_128)), 164 GUS_MAX_DATA_LEN 165 }, 166 // GUS FLOW_CTRL Characteristic - Client Characteristic Configuration Descriptor 167 [GUS_IDX_FLOW_CTRL_CFG] = { 168 ATT_128_CLIENT_CHAR_CFG, 169 READ_PERM_UNSEC | WRITE_REQ_PERM_UNSEC, 170 0, 171 0 172 }, 173 }; 174 175 /**@brief GUS Service interface required by profile manager. */ 176 static ble_prf_manager_cbs_t gus_mgr_cbs = { 177 (prf_init_func_t)gus_init, 178 NULL, 179 NULL, 180 }; 181 182 /**@brief GUS GATT Server Callbacks. */ 183 static gatts_prf_cbs_t gus_gatts_cbs = { 184 gus_read_att_cb, 185 gus_write_att_cb, 186 NULL, 187 gus_ntf_ind_cb, 188 gus_cccd_set_cb 189 }; 190 191 /**@brief GUS Server Information. */ 192 static const prf_server_info_t gus_prf_info = { 193 .max_connection_nb = GUS_CONNECTION_MAX, 194 .manager_cbs = &gus_mgr_cbs, 195 .gatts_prf_cbs = &gus_gatts_cbs 196 }; 197 198 /* 199 * LOCAL FUNCTION DEFINITIONS 200 ***************************************************************************************** 201 */ 202 /** 203 ***************************************************************************************** 204 * @brief Initialize GUS and create DB in ATT. 205 * 206 * @return Error code to know if service initialization succeed or not. 207 ***************************************************************************************** 208 */ 209 static sdk_err_t gus_init(void) 210 { 211 const uint8_t gus_svc_uuid[] = {GUS_SERVICE_UUID}; 212 uint16_t start_hdl = PRF_INVALID_HANDLE; 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; 219 } 220 221 gatts_db.shdl = &start_hdl; 222 gatts_db.uuid = gus_svc_uuid; 223 gatts_db.attr_tab_cfg = (uint8_t *)&s_char_mask; 224 gatts_db.max_nb_attr = GUS_IDX_NB; 225 gatts_db.srvc_perm = SRVC_UUID_TYPE_SET(UUID_TYPE_128); 226 gatts_db.attr_tab_type = SERVICE_TABLE_TYPE_128; 227 gatts_db.attr_tab.attr_tab_128 = gus_att_db; 228 229 error_code = ble_gatts_srvc_db_create(&gatts_db); 230 if (SDK_SUCCESS == error_code) { 231 s_gus_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: Index of the connection. 242 * @param[in] p_param: Pointer to the parameters of the read request. 243 ***************************************************************************************** 244 */ 245 static void gus_read_att_cb(uint8_t conn_idx, const gatts_read_req_cb_t *p_param) 246 { 247 gatts_read_cfm_t cfm; 248 uint16_t handle = p_param->handle; 249 uint8_t tab_index = 0; 250 251 tab_index = prf_find_idx_by_handle(handle, s_gus_env.start_hdl, GUS_IDX_NB, (uint8_t *)&s_char_mask); 252 cfm.handle = handle; 253 cfm.status = BLE_SUCCESS; 254 255 switch (tab_index) { 256 case GUS_IDX_TX_CFG: 257 cfm.length = sizeof(uint16_t); 258 cfm.value = (uint8_t *)&s_gus_env.tx_ntf_cfg[conn_idx]; 259 cfm.status = BLE_SUCCESS; 260 break; 261 262 case GUS_IDX_FLOW_CTRL_CFG: 263 cfm.length = sizeof(uint16_t); 264 cfm.value = (uint8_t *)&s_gus_env.flow_ctrl_ntf_cfg[conn_idx]; 265 cfm.status = BLE_SUCCESS; 266 break; 267 268 default: 269 cfm.length = 0; 270 cfm.status = BLE_ATT_ERR_INVALID_HANDLE; 271 break; 272 } 273 274 ble_gatts_read_cfm(conn_idx, &cfm); 275 } 276 277 void gus_combin_received_packet(uint8_t conn_idx, uint16_t length, uint8_t *p_received_data, uint8_t *p_combin_data) 278 { 279 uint8_t *buffer = p_combin_data; 280 uint8_t ret; 281 282 if (send_data_len[conn_idx] > received_data_len[conn_idx]) { 283 buffer += received_data_len[conn_idx]; 284 ret = memcpy_s(buffer, length, p_received_data, length); 285 if (ret < 0) { 286 return; 287 } 288 received_data_len[conn_idx] += length; 289 } 290 } 291 292 /** 293 ***************************************************************************************** 294 * @brief Handles reception of the write request. 295 * 296 * @param[in] conn_idx: Index of the connection. 297 * @param[in] p_param: Point to the parameters of the write request. 298 ***************************************************************************************** 299 */ 300 static void gus_write_att_cb(uint8_t conn_idx, const gatts_write_req_cb_t *p_param) 301 { 302 uint8_t handle = p_param->handle; 303 uint8_t tab_index = 0; 304 uint16_t cccd_value; 305 gus_evt_t event; 306 gatts_write_cfm_t cfm; 307 uint8_t ret; 308 309 tab_index = prf_find_idx_by_handle(handle, s_gus_env.start_hdl, GUS_IDX_NB, (uint8_t *)&s_char_mask); 310 event.conn_idx = conn_idx; 311 cfm.handle = handle; 312 cfm.status = BLE_SUCCESS; 313 314 switch (tab_index) { 315 case GUS_IDX_RX_VAL: 316 event.evt_type = GUS_EVT_RX_DATA_RECEIVED; 317 if (((uint8_t *)p_param->value)[0] == adv_header) { 318 uint16_t data_len; 319 ret = memcpy(&data_len, DATA_COPY_LEN, &(((uint8_t *)p_param->value)[1]), DATA_COPY_LEN); 320 if (ret < 0) { 321 return; 322 } 323 324 if (data_len <= (MAX_MTU_DEFUALT - MTU_DEF_OFFSET)) { 325 event.p_data = (uint8_t *)p_param->value; 326 event.length = p_param->length; 327 s_gus_env.gus_init.evt_handler(&event); 328 } else { 329 send_data_len[conn_idx] = data_len; 330 gus_combin_received_packet(conn_idx, p_param->length, 331 (uint8_t *)p_param->value, rx_buffer[conn_idx]); 332 if (received_data_len[conn_idx] == send_data_len[conn_idx]) { 333 event.p_data = rx_buffer[conn_idx]; 334 event.length = send_data_len[conn_idx]; 335 s_gus_env.gus_init.evt_handler(&event); 336 received_data_len[conn_idx] = 0; 337 } 338 } 339 } else { 340 gus_combin_received_packet(conn_idx, p_param->length, (uint8_t *)p_param->value, rx_buffer[conn_idx]); 341 342 if (received_data_len[conn_idx] == send_data_len[conn_idx]) { 343 event.p_data = rx_buffer[conn_idx]; 344 event.length = send_data_len[conn_idx]; 345 s_gus_env.gus_init.evt_handler(&event); 346 received_data_len[conn_idx] = 0; 347 } 348 } 349 350 break; 351 352 case GUS_IDX_TX_CFG: 353 cccd_value = le16toh(&p_param->value[0]); 354 event.evt_type = (PRF_CLI_START_NTF == cccd_value) ? GUS_EVT_TX_PORT_OPENED : GUS_EVT_TX_PORT_CLOSED; 355 s_gus_env.tx_ntf_cfg[conn_idx] = cccd_value; 356 s_gus_env.gus_init.evt_handler(&event); 357 break; 358 359 default: 360 cfm.status = BLE_ATT_ERR_INVALID_HANDLE; 361 s_gus_env.gus_init.evt_handler(&event); 362 break; 363 } 364 365 ble_gatts_write_cfm(conn_idx, &cfm); 366 } 367 368 /** 369 ***************************************************************************************** 370 * @brief Handles reception of the cccd recover request. 371 * 372 * @param[in]: conn_idx: Connection index 373 * @param[in]: handle: The handle of cccd attribute. 374 * @param[in]: cccd_value: The value of cccd attribute. 375 ***************************************************************************************** 376 */ 377 static void gus_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value) 378 { 379 uint8_t tab_index = 0; 380 gus_evt_t event; 381 382 if (!prf_is_cccd_value_valid(cccd_value)) { 383 return; 384 } 385 386 tab_index = prf_find_idx_by_handle(handle, s_gus_env.start_hdl, GUS_IDX_NB, (uint8_t *)&s_char_mask); 387 event.conn_idx = conn_idx; 388 event.evt_type = GUS_EVT_INVALID; 389 390 switch (tab_index) { 391 case GUS_IDX_TX_CFG: 392 event.evt_type = (PRF_CLI_START_NTF == cccd_value) ? GUS_EVT_TX_PORT_OPENED : GUS_EVT_TX_PORT_CLOSED; 393 s_gus_env.tx_ntf_cfg[conn_idx] = cccd_value; 394 break; 395 396 case GUS_IDX_FLOW_CTRL_CFG: 397 event.evt_type = (PRF_CLI_START_NTF == cccd_value) ? GUS_EVT_FLOW_CTRL_ENABLE : GUS_EVT_FLOW_CTRL_DISABLE; 398 s_gus_env.flow_ctrl_ntf_cfg[conn_idx] = cccd_value; 399 break; 400 401 default: 402 break; 403 } 404 405 if (GUS_EVT_INVALID != event.evt_type && s_gus_env.gus_init.evt_handler) { 406 s_gus_env.gus_init.evt_handler(&event); 407 } 408 } 409 410 /** 411 ***************************************************************************************** 412 * @brief Handles reception of the complete event. 413 * 414 * @param[in] conn_idx: Connection index. 415 * @param[in] status: The status of GATTC operation. 416 * @param[in] p_ntf_ind: Pointer to the parameters of the complete event. 417 ***************************************************************************************** 418 */ 419 static void gus_ntf_ind_cb(uint8_t conn_idx, uint8_t status, const ble_gatts_ntf_ind_t *p_ntf_ind) 420 { 421 if (s_gus_env.gus_init.evt_handler != NULL) { 422 gus_evt_t event; 423 event.conn_idx = conn_idx; 424 425 if (BLE_SUCCESS == status && BLE_GATT_NOTIFICATION == p_ntf_ind->type) { 426 event.evt_type = GUS_EVT_TX_DATA_SENT; 427 s_gus_env.gus_init.evt_handler(&event); 428 } 429 } 430 } 431 432 /* 433 * GLOBAL FUNCTION DEFINITIONS 434 ***************************************************************************************** 435 */ 436 sdk_err_t gus_tx_data_send(uint8_t conn_idx, uint8_t *p_data, uint16_t length) 437 { 438 sdk_err_t error_code = SDK_ERR_NTF_DISABLED; 439 gatts_noti_ind_t send_cmd; 440 uint8_t *buffer = p_data; 441 442 if (PRF_CLI_START_NTF == s_gus_env.tx_ntf_cfg[conn_idx]) { 443 send_cmd.type = BLE_GATT_NOTIFICATION; 444 send_cmd.handle = prf_find_handle_by_idx(GUS_IDX_TX_VAL, s_gus_env.start_hdl, (uint8_t *)&s_char_mask); 445 446 if (length > (MAX_MTU_DEFUALT - MTU_DEF_OFFSET)) { 447 while length > MAX_MTU_DEFUALT - MTU_DEF_OFFSET) { 448 send_cmd.value = buffer; 449 send_cmd.length = MAX_MTU_DEFUALT - MTU_DEF_OFFSET; 450 error_code = ble_gatts_noti_ind(conn_idx, &send_cmd); 451 buffer += (MAX_MTU_DEFUALT - MTU_DEF_OFFSET); 452 length -= (MAX_MTU_DEFUALT - MTU_DEF_OFFSET); 453 } 454 455 if (length != 0 && length < (MAX_MTU_DEFUALT - MTU_DEF_OFFSET)) { 456 send_cmd.length = length; 457 send_cmd.value = buffer; 458 error_code = ble_gatts_noti_ind(conn_idx, &send_cmd); 459 } 460 } else { 461 send_cmd.value = buffer; 462 send_cmd.length = length; 463 error_code = ble_gatts_noti_ind(conn_idx, &send_cmd); 464 } 465 } 466 467 return error_code; 468 } 469 470 sdk_err_t gus_rx_flow_ctrl_set(uint8_t conn_idx, uint8_t flow_ctrl) 471 { 472 sdk_err_t error_code = BLE_SUCCESS; 473 gatts_noti_ind_t send_cmd; 474 475 if (PRF_CLI_START_NTF == s_gus_env.flow_ctrl_ntf_cfg[conn_idx]) { 476 // Fill in the parameter structure 477 send_cmd.type = BLE_GATT_NOTIFICATION; 478 send_cmd.handle = prf_find_handle_by_idx(GUS_IDX_FLOW_CTRL_VAL, s_gus_env.start_hdl, (uint8_t *)&s_char_mask); 479 480 // Pack measured value in database 481 send_cmd.length = 1; 482 send_cmd.value = &flow_ctrl; 483 484 // Send notification to peer device 485 error_code = ble_gatts_noti_ind(conn_idx, &send_cmd); 486 } 487 488 return error_code; 489 } 490 491 sdk_err_t gus_service_init(gus_init_t *p_gus_init) 492 { 493 sdk_err_t ret; 494 if (p_gus_init == NULL) { 495 return SDK_ERR_POINTER_NULL; 496 } 497 498 ret = memcpy_s(&s_gus_env.gus_init, sizeof(gus_init_t), p_gus_init, sizeof(gus_init_t)); 499 if (ret < 0) { 500 return ret; 501 } 502 return ble_server_prf_add(&gus_prf_info); 503 } 504