1 /** 2 **************************************************************************************** 3 * 4 * @file otas.c 5 * 6 * @brief Over The Air 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 "otas.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 Proprietary UUIDs. */ 52 #define OTA_SERVICE_UUID {0x1B, 0xD7, 0x90, 0xEC, 0xE8, 0xB9, 53 0x75, 0x80, 0x0A, 0x46, 0x44, 0xD3, 0x01, 0x04, 0xED, 0xA6} 54 #define OTA_SERVICE_TX_UUID {0x1B, 0xD7, 0x90, 0xEC, 0xE8, 0xB9, 55 0x75, 0x80, 0x0A, 0x46, 0x44, 0xD3, 0x02, 0x04, 0xED, 0xA6} 56 #define OTA_SERVICE_RX_UUID {0x1B, 0xD7, 0x90, 0xEC, 0xE8, 0xB9, 57 0x75, 0x80, 0x0A, 0x46, 0x44, 0xD3, 0x03, 0x04, 0xED, 0xA6} 58 #define OTA_SERVICE_CTRL_UUID {0x1B, 0xD7, 0x90, 0xEC, 0xE8, 0xB9, 59 0x75, 0x80, 0x0A, 0x46, 0x44, 0xD3, 0x04, 0x04, 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 OTA Service Attributes Indexes. */ 71 enum otas_attr_idx_tag { 72 OTAS_IDX_SVC, 73 74 OTAS_IDX_TX_CHAR, 75 OTAS_IDX_TX_VAL, 76 OTAS_IDX_TX_CFG, 77 OTAS_IDX_RX_CHAR, 78 OTAS_IDX_RX_VAL, 79 OTAS_IDX_CTRL_CHAR, 80 OTAS_IDX_CTRL_VAL, 81 82 OTAS_IDX_NB, 83 }; 84 85 /* 86 * STRUCT DEFINE 87 **************************************************************************************** 88 */ 89 struct otas_env_t { 90 otas_init_t otas_init; 91 uint16_t ntf_cfg[OTAS_CONNECTION_MAX]; 92 uint16_t start_hdl; 93 }; 94 95 96 /* 97 * LOCAL FUNCTION DECLARATION 98 **************************************************************************************** 99 */ 100 static sdk_err_t otas_init(void); 101 static void otas_write_att_cb(uint8_t conn_idx, const gatts_write_req_cb_t *p_param); 102 static void otas_read_att_cb(uint8_t conn_idx, const gatts_read_req_cb_t *p_param); 103 static void otas_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value); 104 static void otas_gatts_cmpl_cb(uint8_t conn_idx, uint8_t status, const ble_gatts_ntf_ind_t *p_ntf_ind); 105 106 /* 107 * LOCAL VARIABLE DEFINITIONS 108 **************************************************************************************** 109 */ 110 static struct otas_env_t s_otas_env; 111 static uint16_t s_char_mask = 0xff; 112 /**@brief Full OTAS Database Description - Used to add attributes into the database. */ 113 static const attm_desc_128_t otas_att_db[OTAS_IDX_NB] = { 114 // OTA service 115 [OTAS_IDX_SVC] = {ATT_128_PRIMARY_SERVICE, READ_PERM_UNSEC, 0, 0}, 116 117 // OTA TX Characteristic Declaration 118 [OTAS_IDX_TX_CHAR] = {ATT_128_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0}, 119 // OTA TX Characteristic Value 120 [OTAS_IDX_TX_VAL] = { 121 OTA_SERVICE_TX_UUID, 122 NOTIFY_PERM_UNSEC, 123 (ATT_VAL_LOC_USER | ATT_UUID_TYPE_SET(UUID_TYPE_128)), 124 OTAS_MAX_DATA_LEN 125 }, 126 127 // OTA TX Characteristic - Client Characteristic Configuration Descriptor 128 [OTAS_IDX_TX_CFG] = { 129 ATT_128_CLIENT_CHAR_CFG, 130 READ_PERM_UNSEC| WRITE_REQ_PERM_UNSEC, 131 0, 132 0 133 }, 134 135 // OTA RX Characteristic Declaration 136 [OTAS_IDX_RX_CHAR] = {ATT_128_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0 }, 137 138 // OTA RX Characteristic Value 139 [OTAS_IDX_RX_VAL] = { 140 OTA_SERVICE_RX_UUID, 141 WRITE_CMD_PERM_UNSEC, 142 (ATT_VAL_LOC_USER | ATT_UUID_TYPE_SET(UUID_TYPE_128)), 143 OTAS_MAX_DATA_LEN 144 }, 145 146 // OTA CTRL Characteristic Declaration 147 [OTAS_IDX_CTRL_CHAR] = {ATT_128_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0}, 148 149 // OTA CTRL Characteristic Value 150 [OTAS_IDX_CTRL_VAL] = { 151 OTA_SERVICE_CTRL_UUID, 152 WRITE_CMD_PERM_UNSEC, 153 (ATT_VAL_LOC_USER | ATT_UUID_TYPE_SET(UUID_TYPE_128)), 154 sizeof(uint32_t) 155 }, 156 }; 157 158 /**@brief OTA Server Task interface required by profile manager. */ 159 static ble_prf_manager_cbs_t otas_tack_cbs = { 160 (prf_init_func_t) otas_init, 161 NULL, 162 NULL, 163 }; 164 165 /**@brief OTA Server Task Callbacks. */ 166 static gatts_prf_cbs_t otas_cb_func = { 167 otas_read_att_cb, 168 otas_write_att_cb, 169 NULL, 170 otas_gatts_cmpl_cb, 171 otas_cccd_set_cb 172 }; 173 174 /**@brief OTA Server Information. */ 175 static const prf_server_info_t otas_prf_info = { 176 .max_connection_nb = OTAS_CONNECTION_MAX, 177 .manager_cbs = &otas_tack_cbs, 178 .gatts_prf_cbs =&otas_cb_func 179 }; 180 181 /* 182 * LOCAL FUNCTION DEFINITIONS 183 **************************************************************************************** 184 */ 185 /** 186 ***************************************************************************************** 187 * @brief Initialize OTA service create db in att 188 * 189 * @return Error code to know if profile initialization succeed or not. 190 ***************************************************************************************** 191 */ 192 static sdk_err_t otas_init(void) 193 { 194 // The start hanlde must be set with PRF_INVALID_HANDLE to be allocated automatically by BLE Stack. 195 uint16_t start_hdl = PRF_INVALID_HANDLE; 196 const uint8_t otas_svc_uuid[] = OTA_SERVICE_UUID; 197 sdk_err_t error_code = SDK_SUCCESS; 198 gatts_create_db_t gatts_db; 199 200 error_code = memset_s(&gatts_db, sizeof(gatts_db), 0, sizeof(gatts_db)); 201 if (error_code < 0) { 202 return error_code; 203 } 204 205 gatts_db.shdl = &start_hdl; 206 gatts_db.uuid = otas_svc_uuid; 207 gatts_db.attr_tab_cfg = (uint8_t*)&s_char_mask; 208 gatts_db.max_nb_attr = OTAS_IDX_NB; 209 gatts_db.srvc_perm = SRVC_UUID_TYPE_SET(UUID_TYPE_128); 210 gatts_db.attr_tab_type = SERVICE_TABLE_TYPE_128; 211 gatts_db.attr_tab.attr_tab_128 = otas_att_db; 212 213 error_code = ble_gatts_srvc_db_create(&gatts_db); 214 if (SDK_SUCCESS == error_code) { 215 s_otas_env.start_hdl = *(gatts_db.shdl); 216 } 217 218 return error_code; 219 } 220 221 /** 222 ***************************************************************************************** 223 * @brief Handles reception of the attribute info request message. 224 * 225 * @param[in] conn_idx: Connection index 226 * @param[in] p_param: The parameters of the read request. 227 ***************************************************************************************** 228 */ 229 static void otas_read_att_cb(uint8_t conn_idx, const gatts_read_req_cb_t *p_param) 230 { 231 gatts_read_cfm_t cfm; 232 uint8_t handle = p_param->handle; 233 uint8_t tab_index = 0; 234 235 tab_index = prf_find_idx_by_handle(handle, s_otas_env.start_hdl, OTAS_IDX_NB, (uint8_t*)&s_char_mask); 236 237 cfm.handle = handle; 238 cfm.status = BLE_SUCCESS; 239 240 switch (tab_index) { 241 case OTAS_IDX_TX_CFG: 242 cfm.length = sizeof(uint16_t); 243 cfm.value = (uint8_t *)(&s_otas_env.ntf_cfg[conn_idx]); 244 break; 245 246 default: 247 cfm.length = 0; 248 cfm.status = BLE_ATT_ERR_INVALID_HANDLE; 249 break; 250 } 251 252 ble_gatts_read_cfm(conn_idx, &cfm); 253 } 254 255 /** 256 ***************************************************************************************** 257 * @brief Handles reception of the write request. 258 * 259 * @param[in] conn_idx: of connection index 260 * @param[in] p_param: Pointer to the parameters of the write request. 261 ***************************************************************************************** 262 */ 263 static void otas_write_att_cb(uint8_t conn_idx, const gatts_write_req_cb_t *p_param) 264 { 265 gatts_write_cfm_t cfm; 266 uint8_t handle = p_param->handle; 267 uint8_t tab_index = 0; 268 uint16_t cccd_value = 0; 269 otas_evt_t event; 270 271 tab_index = prf_find_idx_by_handle(handle, s_otas_env.start_hdl, OTAS_IDX_NB, (uint8_t*)&s_char_mask); 272 273 cfm.handle = handle; 274 cfm.status = BLE_SUCCESS; 275 276 switch (tab_index) { 277 case OTAS_IDX_RX_VAL: 278 if (s_otas_env.otas_init.evt_handler != NULL) { 279 event.conn_idx = conn_idx; 280 event.evt_type = OTAS_EVT_RX_RECEIVE_DATA; 281 event.p_data = (uint8_t*)p_param->value; 282 event.length = p_param->length; 283 284 s_otas_env.otas_init.evt_handler(&event); 285 } 286 287 break; 288 289 case OTAS_IDX_TX_CFG: 290 cccd_value = le16toh(&p_param->value[0]); 291 if (s_otas_env.otas_init.evt_handler != NULL) { 292 event.conn_idx = conn_idx; 293 event.evt_type = (cccd_value == PRF_CLI_START_NTF) ?\ 294 OTAS_EVT_TX_NOTIFICATION_ENABLED : 295 OTAS_EVT_TX_NOTIFICATION_DISABLED; 296 s_otas_env.otas_init.evt_handler(&event); 297 } 298 s_otas_env.ntf_cfg[conn_idx] = cccd_value; 299 break; 300 301 case OTAS_IDX_CTRL_VAL: 302 if (le32toh(&p_param->value[0]) == OTAS_CTRL_ENTER_DFU) { 303 if (s_otas_env.otas_init.evt_handler != NULL) { 304 event.conn_idx = conn_idx; 305 event.evt_type = OTAS_EVT_DFU_MODE_ENTER; 306 s_otas_env.otas_init.evt_handler(&event); 307 } 308 } 309 break; 310 311 default: 312 cfm.status = BLE_ATT_ERR_INVALID_HANDLE; 313 break; 314 } 315 316 ble_gatts_write_cfm(conn_idx, &cfm); 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 */ 328 static void otas_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value) 329 { 330 uint8_t tab_index = 0; 331 otas_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, s_otas_env.start_hdl, OTAS_IDX_NB, (uint8_t*)&s_char_mask); 338 339 switch (tab_index) { 340 case OTAS_IDX_TX_CFG: 341 if (s_otas_env.otas_init.evt_handler != NULL) { 342 event.conn_idx = conn_idx; 343 event.evt_type = (cccd_value == PRF_CLI_START_NTF) ?\ 344 OTAS_EVT_TX_NOTIFICATION_ENABLED : 345 OTAS_EVT_TX_NOTIFICATION_DISABLED; 346 s_otas_env.otas_init.evt_handler(&event); 347 } 348 s_otas_env.ntf_cfg[conn_idx] = cccd_value; 349 break; 350 351 default: 352 break; 353 } 354 } 355 356 /** 357 ***************************************************************************************** 358 * @brief Handles reception of the complete event. 359 * 360 * @param[in] conn_idx: Connection index 361 * @param[in] p_param: Pointer to the parameters of the complete event. 362 * 363 * @return If the event was consumed or not. 364 ***************************************************************************************** 365 */ 366 static void otas_gatts_cmpl_cb(uint8_t conn_idx, uint8_t status, const ble_gatts_ntf_ind_t *p_ntf_ind) 367 { 368 if (s_otas_env.otas_init.evt_handler != NULL) { 369 otas_evt_t event; 370 371 event.conn_idx = conn_idx; 372 if (status == BLE_SUCCESS) { 373 if (p_ntf_ind->type == BLE_GATT_NOTIFICATION) { 374 event.evt_type = OTAS_EVT_NOTIFY_COMPLETE; 375 s_otas_env.otas_init.evt_handler(&event); 376 } 377 } 378 } 379 } 380 381 /* 382 * GLOBAL FUNCTION DEFINITIONS 383 **************************************************************************************** 384 */ 385 sdk_err_t otas_notify_tx_data(uint8_t conn_idx, uint8_t* p_data, uint16_t len) 386 { 387 sdk_err_t error_code = SDK_ERR_NTF_DISABLED; 388 gatts_noti_ind_t send_cmd; 389 390 if (s_otas_env.ntf_cfg[conn_idx] == PRF_CLI_START_NTF) { 391 // Fill in the parameter structure 392 send_cmd.type = BLE_GATT_NOTIFICATION; 393 send_cmd.handle = prf_find_handle_by_idx(OTAS_IDX_TX_VAL, s_otas_env.start_hdl, (uint8_t*)&s_char_mask); 394 // pack measured value in database 395 send_cmd.length = len; 396 send_cmd.value = p_data; 397 // send notification to peer device 398 error_code = ble_gatts_noti_ind(conn_idx, &send_cmd); 399 } 400 return error_code; 401 } 402 403 404 sdk_err_t otas_service_init(otas_init_t *p_otas_init) 405 { 406 if (p_otas_init == NULL) { 407 return SDK_ERR_POINTER_NULL; 408 } 409 410 s_otas_env.otas_init.evt_handler = p_otas_init->evt_handler; 411 412 return ble_server_prf_add(&otas_prf_info); 413 } 414