ble_conn_params.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. /**
  2. * Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
  3. *
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without modification,
  7. * are permitted provided that the following conditions are met:
  8. *
  9. * 1. Redistributions of source code must retain the above copyright notice, this
  10. * list of conditions and the following disclaimer.
  11. *
  12. * 2. Redistributions in binary form, except as embedded into a Nordic
  13. * Semiconductor ASA integrated circuit in a product or a software update for
  14. * such product, must reproduce the above copyright notice, this list of
  15. * conditions and the following disclaimer in the documentation and/or other
  16. * materials provided with the distribution.
  17. *
  18. * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
  19. * contributors may be used to endorse or promote products derived from this
  20. * software without specific prior written permission.
  21. *
  22. * 4. This software, with or without modification, must only be used with a
  23. * Nordic Semiconductor ASA integrated circuit.
  24. *
  25. * 5. Any software provided in binary form under this license must not be reverse
  26. * engineered, decompiled, modified and/or disassembled.
  27. *
  28. * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
  29. * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  30. * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
  31. * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
  32. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  33. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  34. * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  35. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  36. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  37. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  38. *
  39. */
  40. #include "sdk_common.h"
  41. #if NRF_MODULE_ENABLED(NRF_BLE_CONN_PARAMS)
  42. #include <stdlib.h>
  43. #include "nrf.h"
  44. #include "sdk_errors.h"
  45. #include "ble_hci.h"
  46. #include "ble_err.h"
  47. #include "ble_conn_params.h"
  48. #include "ble_srv_common.h"
  49. #include "ble_conn_state.h"
  50. #include "nrf_sdh_ble.h"
  51. #include "app_timer.h"
  52. #include "app_util.h"
  53. #define NRF_BLE_CONN_PARAMS_INSTANCE_COUNT NRF_SDH_BLE_PERIPHERAL_LINK_COUNT //!< The number of @ref ble_conn_params_instance_t instances kept by the conn_params module.
  54. #if (NRF_BLE_CONN_PARAMS_INSTANCE_COUNT < 1)
  55. #error Invalid NRF_SDH_BLE_PERIPHERAL_LINK_COUNT value. Set it in SDK config (nrf_sdh_ble).
  56. #endif
  57. /** @brief Each peripheral link has such an instance associated with it.
  58. */
  59. typedef struct
  60. {
  61. uint16_t conn_handle; //!< The connection handle of this link. If this is @ref BLE_CONN_HANDLE_INVALID, the instance is free.
  62. app_timer_id_t timer_id; //!< The ID of the timer associated with this link.
  63. uint8_t update_count; //!< The number of times the connection parameters have been attempted negotiated on this link.
  64. uint8_t params_ok; //!< Whether the current connection parameters on this link are acceptable according to the @p preferred_conn_params, and configured maximum deviations.
  65. ble_gap_conn_params_t preferred_conn_params; //!< The desired connection parameters for this link.
  66. } ble_conn_params_instance_t;
  67. static app_timer_t m_timer_data[NRF_BLE_CONN_PARAMS_INSTANCE_COUNT] = {{{0}}}; //!< Data needed for timers.
  68. static ble_conn_params_instance_t m_conn_params_instances[NRF_BLE_CONN_PARAMS_INSTANCE_COUNT] = {{0}}; //!< Configuration data for each connection.
  69. static ble_conn_params_init_t m_conn_params_config; //!< Configuration as provided by the application during intialization.
  70. static ble_gap_conn_params_t m_preferred_conn_params; //!< The preferred connection parameters as specified during initialization.
  71. //lint -esym(551, m_preferred_conn_params) "Not accessed"
  72. /**@brief Function for retrieving the conn_params instance belonging to a conn_handle
  73. *
  74. * @params[in] conn_handle The connection handle to retrieve the instance of.
  75. *
  76. * @return A pointer to the instance, or NULL if no instance was found with that conn_handle.
  77. */
  78. static ble_conn_params_instance_t * instance_get(uint16_t conn_handle)
  79. {
  80. //lint -save -e681 "Loop not entered" when NRF_BLE_CONN_PARAMS_INSTANCE_COUNT is 0
  81. for (uint32_t i = 0; i < NRF_BLE_CONN_PARAMS_INSTANCE_COUNT; i++)
  82. {
  83. if (m_conn_params_instances[i].conn_handle == conn_handle)
  84. {
  85. return &m_conn_params_instances[i];
  86. }
  87. }
  88. //lint -restore
  89. return NULL;
  90. }
  91. /**@brief Function for initializing an instance, and associating it with a conn_handle.
  92. *
  93. * @params[in] p_instance The instance to initialize and associate.
  94. * @params[in] conn_handle The connection handle to associate with.
  95. */
  96. static __INLINE void instance_claim(ble_conn_params_instance_t * p_instance, uint16_t conn_handle)
  97. {
  98. p_instance->conn_handle = conn_handle;
  99. p_instance->update_count = 0;
  100. p_instance->preferred_conn_params = m_preferred_conn_params;
  101. }
  102. /**@brief Function for freeing an instance.
  103. *
  104. * @params[in] p_instance The instance to free.
  105. */
  106. static __INLINE void instance_free(ble_conn_params_instance_t * p_instance)
  107. {
  108. p_instance->conn_handle = BLE_CONN_HANDLE_INVALID;
  109. }
  110. /**@brief Function for validating a set of connection parameters against the preferred parameters.
  111. *
  112. * @param[in] p_preferred_conn_params The desired parameters.
  113. * @param[in] p_actual_conn_params The parameters to validate.
  114. * @param[in] max_slave_latency_err The amount of discrepancy in slave latency, in number of
  115. * connection intervals, that will be accepted.
  116. * @param[in] max_sup_timeout_err The amount of discrepancy in supervision timeout, in tens of
  117. * milliseconds, that will be accepted.
  118. *
  119. * @return Whether the params in @p p_actual_conn_params are acceptable given the other parameters.
  120. */
  121. static bool is_conn_params_ok(ble_gap_conn_params_t const * p_preferred_conn_params,
  122. ble_gap_conn_params_t const * p_actual_conn_params,
  123. uint16_t max_slave_latency_err,
  124. uint16_t max_sup_timeout_err)
  125. {
  126. uint32_t max_allowed_sl = p_preferred_conn_params->slave_latency + max_slave_latency_err;
  127. uint32_t min_allowed_sl = p_preferred_conn_params->slave_latency
  128. - MIN(max_slave_latency_err, p_preferred_conn_params->slave_latency);
  129. uint32_t max_allowed_to = p_preferred_conn_params->conn_sup_timeout + max_sup_timeout_err;
  130. uint32_t min_allowed_to = p_preferred_conn_params->conn_sup_timeout
  131. - MIN(max_sup_timeout_err, p_preferred_conn_params->conn_sup_timeout);
  132. // Check if interval is within the acceptable range.
  133. // NOTE: Using max_conn_interval in the received event data because this contains
  134. // the client's connection interval.
  135. if ((p_actual_conn_params->max_conn_interval < p_preferred_conn_params->min_conn_interval)
  136. || (p_actual_conn_params->max_conn_interval > p_preferred_conn_params->max_conn_interval))
  137. {
  138. return false;
  139. }
  140. // Check if slave latency is within the acceptable deviation.
  141. if ((p_actual_conn_params->slave_latency < min_allowed_sl)
  142. || (p_actual_conn_params->slave_latency > max_allowed_sl))
  143. {
  144. return false;
  145. }
  146. // Check if supervision timeout is within the acceptable deviation.
  147. if ((p_actual_conn_params->conn_sup_timeout < min_allowed_to)
  148. || (p_actual_conn_params->conn_sup_timeout > max_allowed_to))
  149. {
  150. return false;
  151. }
  152. return true;
  153. }
  154. static void send_error_evt(ret_code_t err_code)
  155. {
  156. if (m_conn_params_config.error_handler != NULL)
  157. {
  158. m_conn_params_config.error_handler(err_code);
  159. }
  160. }
  161. /**@brief Function for sending a conn_param_update request on-air, and handling errors.
  162. *
  163. * @param[in] conn_handle Connection to send request on.
  164. * @param[in] p_new_conn_params Connection parameters to request.
  165. *
  166. * @return Whether the request was successfully sent.
  167. */
  168. static bool send_update_request(uint16_t conn_handle, ble_gap_conn_params_t * p_new_conn_params)
  169. {
  170. ret_code_t err_code;
  171. err_code = sd_ble_gap_conn_param_update(conn_handle, p_new_conn_params);
  172. if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_BUSY)) // NRF_ERROR_BUSY means another conn_param_update request is pending.
  173. {
  174. send_error_evt(err_code);
  175. }
  176. return (err_code == NRF_SUCCESS);
  177. }
  178. /**@brief Function called after conn_params_update_delay has happened. This is triggered by app_timer.
  179. *
  180. * @param[in] p_context Context identifying which connection this is for.
  181. */
  182. static void update_timeout_handler(void * p_context)
  183. {
  184. uint32_t conn_handle = (uint32_t)p_context;
  185. ble_conn_params_instance_t * p_instance = instance_get(conn_handle);
  186. if (p_instance != NULL)
  187. {
  188. // Check if we have reached the maximum number of attempts
  189. if (p_instance->update_count < m_conn_params_config.max_conn_params_update_count)
  190. {
  191. bool update_sent = send_update_request(conn_handle, &p_instance->preferred_conn_params);
  192. if (update_sent)
  193. {
  194. p_instance->update_count++;
  195. }
  196. }
  197. else
  198. {
  199. p_instance->update_count = 0;
  200. // Negotiation failed, disconnect automatically if this has been configured
  201. if (m_conn_params_config.disconnect_on_fail)
  202. {
  203. ret_code_t err_code;
  204. err_code = sd_ble_gap_disconnect(conn_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE);
  205. if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE)) // NRF_ERROR_INVALID_STATE means disconnect is already in progress.
  206. {
  207. send_error_evt(err_code);
  208. }
  209. }
  210. // Notify the application that the procedure has failed
  211. if (m_conn_params_config.evt_handler != NULL)
  212. {
  213. ble_conn_params_evt_t evt;
  214. evt.evt_type = BLE_CONN_PARAMS_EVT_FAILED;
  215. evt.conn_handle = conn_handle;
  216. m_conn_params_config.evt_handler(&evt);
  217. }
  218. }
  219. }
  220. }
  221. ret_code_t ble_conn_params_init(const ble_conn_params_init_t * p_init)
  222. {
  223. ret_code_t err_code;
  224. VERIFY_PARAM_NOT_NULL(p_init);
  225. m_conn_params_config = *p_init;
  226. m_conn_params_config.p_conn_params = &m_preferred_conn_params;
  227. if (p_init->p_conn_params != NULL)
  228. {
  229. // Set the connection params in stack.
  230. err_code = sd_ble_gap_ppcp_set(p_init->p_conn_params);
  231. if (err_code != NRF_SUCCESS)
  232. {
  233. return err_code;
  234. }
  235. m_preferred_conn_params = *p_init->p_conn_params;
  236. }
  237. else
  238. {
  239. // Get the (default) connection params from stack.
  240. err_code = sd_ble_gap_ppcp_get(&m_preferred_conn_params);
  241. if (err_code != NRF_SUCCESS)
  242. {
  243. return err_code;
  244. }
  245. }
  246. //lint -save -e681 "Loop not entered" when NRF_BLE_CONN_PARAMS_INSTANCE_COUNT is 0
  247. for (uint32_t i = 0; i < NRF_BLE_CONN_PARAMS_INSTANCE_COUNT; i++)
  248. {
  249. ble_conn_params_instance_t * p_instance = &m_conn_params_instances[i];
  250. instance_free(p_instance);
  251. p_instance->timer_id = &m_timer_data[i];
  252. err_code = app_timer_create(&p_instance->timer_id,
  253. APP_TIMER_MODE_SINGLE_SHOT,
  254. update_timeout_handler);
  255. if (err_code != NRF_SUCCESS)
  256. {
  257. return NRF_ERROR_INTERNAL;
  258. }
  259. }
  260. //lint -restore
  261. return NRF_SUCCESS;
  262. }
  263. ret_code_t ble_conn_params_stop(void)
  264. {
  265. ret_code_t err_code;
  266. //lint -save -e681 "Loop not entered" when NRF_BLE_CONN_PARAMS_INSTANCE_COUNT is 0
  267. for (uint32_t i = 0; i < NRF_BLE_CONN_PARAMS_INSTANCE_COUNT; i++)
  268. {
  269. err_code = app_timer_stop(m_conn_params_instances[i].timer_id);
  270. switch (err_code)
  271. {
  272. case NRF_SUCCESS:
  273. /* do nothing */
  274. break;
  275. case NRF_ERROR_INVALID_STATE:
  276. /* do nothing */
  277. break;
  278. case NRF_ERROR_NO_MEM:
  279. return NRF_ERROR_BUSY;
  280. case NRF_ERROR_INVALID_PARAM:
  281. /* fallthrough */
  282. default:
  283. return NRF_ERROR_INTERNAL;
  284. }
  285. }
  286. //lint -restore
  287. return NRF_SUCCESS;
  288. }
  289. /**@brief Function for taking appropriate action based on the current state of connection parameters.
  290. *
  291. * @param[in] conn_handle Connection to handle.
  292. * @param[in] p_instance Configuration for the connection.
  293. */
  294. static void conn_params_negotiation(uint16_t conn_handle, ble_conn_params_instance_t * p_instance)
  295. {
  296. // Start negotiation if the received connection parameters are not acceptable
  297. if (!p_instance->params_ok)
  298. {
  299. ret_code_t err_code;
  300. uint32_t timeout_ticks;
  301. if (p_instance->update_count == 0)
  302. {
  303. // First connection parameter update
  304. timeout_ticks = m_conn_params_config.first_conn_params_update_delay;
  305. }
  306. else
  307. {
  308. timeout_ticks = m_conn_params_config.next_conn_params_update_delay;
  309. }
  310. err_code = app_timer_start(p_instance->timer_id, timeout_ticks, (void *)(uint32_t)conn_handle);
  311. if (err_code != NRF_SUCCESS)
  312. {
  313. send_error_evt(err_code);
  314. }
  315. }
  316. else
  317. {
  318. p_instance->update_count = 0;
  319. // Notify the application that the procedure has succeeded
  320. if (m_conn_params_config.evt_handler != NULL)
  321. {
  322. ble_conn_params_evt_t evt;
  323. evt.evt_type = BLE_CONN_PARAMS_EVT_SUCCEEDED;
  324. evt.conn_handle = conn_handle;
  325. m_conn_params_config.evt_handler(&evt);
  326. }
  327. }
  328. }
  329. /**@brief Function for handling a connection event from the SoftDevice.
  330. *
  331. * @param[in] p_ble_evt Event from the SoftDevice.
  332. */
  333. static void on_connect(ble_evt_t const * p_ble_evt)
  334. {
  335. uint8_t role = p_ble_evt->evt.gap_evt.params.connected.role;
  336. uint16_t conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
  337. if (role != BLE_GAP_ROLE_PERIPH)
  338. {
  339. return;
  340. }
  341. ble_conn_params_instance_t * p_instance = instance_get(BLE_CONN_HANDLE_INVALID);
  342. if (p_instance == NULL)
  343. {
  344. send_error_evt(NRF_ERROR_NO_MEM);
  345. return;
  346. }
  347. instance_claim(p_instance, conn_handle);
  348. p_instance->params_ok = is_conn_params_ok(&p_instance->preferred_conn_params,
  349. &p_ble_evt->evt.gap_evt.params.connected.conn_params,
  350. NRF_BLE_CONN_PARAMS_MAX_SLAVE_LATENCY_DEVIATION,
  351. NRF_BLE_CONN_PARAMS_MAX_SUPERVISION_TIMEOUT_DEVIATION);
  352. // Check if we shall handle negotiation on connect
  353. if (m_conn_params_config.start_on_notify_cccd_handle == BLE_GATT_HANDLE_INVALID)
  354. {
  355. conn_params_negotiation(conn_handle, p_instance);
  356. }
  357. }
  358. /**@brief Function for handling a disconnection event from the SoftDevice.
  359. *
  360. * @param[in] p_ble_evt Event from the SoftDevice.
  361. */
  362. static void on_disconnect(ble_evt_t const * p_ble_evt)
  363. {
  364. ret_code_t err_code;
  365. uint16_t conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
  366. ble_conn_params_instance_t * p_instance = instance_get(conn_handle);
  367. if (p_instance != NULL)
  368. {
  369. // Stop timer if running
  370. err_code = app_timer_stop(p_instance->timer_id);
  371. if (err_code != NRF_SUCCESS)
  372. {
  373. send_error_evt(err_code);
  374. }
  375. instance_free(p_instance);
  376. }
  377. }
  378. /**@brief Function for handling a GATT write event from the SoftDevice.
  379. *
  380. * @details To provide the start_on_notify_cccd_handle functionality.
  381. *
  382. * @param[in] p_ble_evt Event from the SoftDevice.
  383. */
  384. static void on_write(ble_evt_t const * p_ble_evt)
  385. {
  386. ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
  387. // Check if this is the correct CCCD
  388. if (
  389. (p_evt_write->handle == m_conn_params_config.start_on_notify_cccd_handle)
  390. &&
  391. (p_evt_write->len == 2)
  392. )
  393. {
  394. uint16_t conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
  395. ble_conn_params_instance_t * p_instance = instance_get(conn_handle);
  396. if (p_instance != NULL)
  397. {
  398. // Check if this is a 'start notification'
  399. if (ble_srv_is_notification_enabled(p_evt_write->data))
  400. {
  401. // Do connection parameter negotiation if necessary
  402. conn_params_negotiation(conn_handle, p_instance);
  403. }
  404. else
  405. {
  406. ret_code_t err_code;
  407. // Stop timer if running
  408. err_code = app_timer_stop(p_instance->timer_id);
  409. if (err_code != NRF_SUCCESS)
  410. {
  411. send_error_evt(err_code);
  412. }
  413. }
  414. }
  415. }
  416. }
  417. /**@brief Function for handling a connection parameter update event from the SoftDevice.
  418. *
  419. * @details This event means the peer central has changed the connection parameters or declined our
  420. * request.
  421. *
  422. * @param[in] p_ble_evt Event from the SoftDevice.
  423. */
  424. static void on_conn_params_update(ble_evt_t const * p_ble_evt)
  425. {
  426. uint16_t conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
  427. ble_conn_params_instance_t * p_instance = instance_get(conn_handle);
  428. if (p_instance != NULL)
  429. {
  430. p_instance->params_ok = is_conn_params_ok(
  431. &p_instance->preferred_conn_params,
  432. &p_ble_evt->evt.gap_evt.params.conn_param_update.conn_params,
  433. NRF_BLE_CONN_PARAMS_MAX_SLAVE_LATENCY_DEVIATION,
  434. NRF_BLE_CONN_PARAMS_MAX_SUPERVISION_TIMEOUT_DEVIATION);
  435. conn_params_negotiation(conn_handle, p_instance);
  436. }
  437. }
  438. /**
  439. * @brief Function for handling BLE events.
  440. *
  441. * @param[in] p_ble_evt Event received from the BLE stack.
  442. * @param[in] p_context Context.
  443. */
  444. static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
  445. {
  446. switch (p_ble_evt->header.evt_id)
  447. {
  448. case BLE_GAP_EVT_CONNECTED:
  449. on_connect(p_ble_evt);
  450. break;
  451. case BLE_GAP_EVT_DISCONNECTED:
  452. on_disconnect(p_ble_evt);
  453. break;
  454. case BLE_GATTS_EVT_WRITE:
  455. on_write(p_ble_evt);
  456. break;
  457. case BLE_GAP_EVT_CONN_PARAM_UPDATE:
  458. on_conn_params_update(p_ble_evt);
  459. break;
  460. default:
  461. // No implementation needed.
  462. break;
  463. }
  464. }
  465. ret_code_t ble_conn_params_change_conn_params(uint16_t conn_handle,
  466. ble_gap_conn_params_t * p_new_params)
  467. {
  468. ret_code_t err_code = BLE_ERROR_INVALID_CONN_HANDLE;
  469. ble_conn_params_instance_t * p_instance = instance_get(conn_handle);
  470. if (p_new_params == NULL)
  471. {
  472. p_new_params = &m_preferred_conn_params;
  473. }
  474. if (p_instance != NULL)
  475. {
  476. // Send request to central.
  477. err_code = sd_ble_gap_conn_param_update(conn_handle, p_new_params);
  478. if (err_code == NRF_SUCCESS)
  479. {
  480. p_instance->params_ok = false;
  481. p_instance->update_count = 1;
  482. p_instance->preferred_conn_params = *p_new_params;
  483. }
  484. }
  485. return err_code;
  486. }
  487. NRF_SDH_BLE_OBSERVER(m_ble_observer, BLE_CONN_PARAMS_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
  488. #endif //ENABLED